• 文件I/O实践(1) --基础API


    什么是I/O

    输入/输出是内存和外设之间拷贝数据的过程:

       设备->内存: 输入操作

       内存->设备: 输出操作


     高级I/O: ANSI C提供的标准I/O库函数成为高级I/O, 也称为带缓冲的I/O;

     低级I/O: Linux 提供的系统调用通常也称为不带缓冲的I/O;

     

    文件描述符

      对于Linux内核而言, 所有的文件或设备都对应一个文件描述符(Linux的设计哲学: 一切皆文件), 这样可以简化系统编程的复杂程度;

      当打开/创建一个文件的时候, 内核向进程返回一个文件描述符(是一个非负整数). 后续对文件的操作只需通过该文件描述符即可进行, 内核记录有关这个打开文件的信息;

      一个进程启动时, 默认已经打开了3个文件, 标准输入(0, STDIN_FILENO), 标准输出(1, STDOUT_FILENO), 标准错误输出(2, STDERR_FILENO), 这些常量定义在unistd.h头文件中; 

      其中, 文件描述符基本上是与文件描述指针(FILE*)一一对应的, 如文件描述符0,1,2 对应 stdin, stdout, stderr;

     

    文件指针与文件描述符的转换

    fileno: 将文件指针转换成文件描述符

           int fileno(FILE *stream);

    fdopen: 将文件描述符转换成文件指针

           FILE *fdopen(int fd, const char *mode);

    //示例
    int main()
    {
        cout << "fileno(stdin) = " << fileno(stdin) << endl;
        cout << "fileno(stdout) = " << fileno(stdout) << endl;
        cout << "fileno(stderr) = " << fileno(stderr) << endl;
        return 0;
    }

    文件I/O API

    1.open

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);

    参数:

       pathname:  文件名, 可以包含[绝对/相对]路径名;

       flags: 文件打开模式;

       mode: 用来指定对文件所有者, 文件用户组以及系统中的其他用户的访问权限;

    注意: newMode = mode & ~umask

     

    flags常用值


    //示例1
    int main()
    {
        int fd = open("test.txt", O_RDONLY);
        if (fd == -1)
        {
            cerr << "file open error, errno = " << errno <<
                 "
    strerror: " << strerror(errno) << endl;
            perror("perror");
            exit(EXIT_FAILURE);
        }
        cout << "file open success" << endl;
    }
    //示例2
    inline void err_exit(std::string message)
    {
        perror(message.c_str());
        exit(EXIT_FAILURE);
    }
    
    int main()
    {
        umask(0000);
        int fd = open("test.txt", O_RDWR|O_CREAT|O_EXCL, 0666);
        if (fd == -1)
            err_exit("file open error");
        else
            cout << "file descriptor = " << fd << endl;
    }

    [附]

    (1). umask API

       //改变umask值

       mode_t umask(mode_t mask);

    (2). ulimit -a

       查看系统中的各种限制;

       其中-n: 查看一个进程所能够打开的最大文件数

    (3). cat /proc/sys/fs/file-max 

       查看一个系统能够支持的最大打开文件数(该数与内存大小有关)


    2.close

    #include <unistd.h>
    int close(int fd);

    关闭文件描述符, 使得文件描述符得以重新利用

     

    3.read

    ssize_t read(int fd, void *buf, size_t count);

    返回值:

      错误: -1

      到达文件尾: 0

      成功: 返回从文件复制到规定缓冲区的字节数

    4.wirte

    ssize_t write(int fd, const void *buf, size_t count);

    返回值:

       错误: -1

       什么都没做: 0

       成功: 返回成功写入文件的字节数

     

    注意:

       write返回大于0时, 并不代表buf的内容已经写入到磁盘上的文件中了, 其仅仅代表buf中的数据已经copy到相应的内核缓冲区了. 要实现将缓冲区的内容真正”冲洗”到磁盘上的文件, 需要调用fsync函数;

         int fsync(int fd);

       其将内核缓冲区中尚未写入磁盘的内容同步到文件系统中;

       其实在open调用的时候也可以指定同步选项:O_SYNC  O_SYNC The file is opened for synchronous I/O.   Any  write(2)s  on  the  resulting  file  descriptor will block the calling process until the data has been physically written to the underlying hardware.

       write会等到将buf的内容真正的写入到磁盘才真正返回;

    //示例: 带有O_SYNC选项
    int main(int argc, char *argv[])
    {
        if (argc < 3)
        {
            cerr << "Usage : " << argv[0] << " src dest" << endl;
            exit(EXIT_FAILURE);
        }
    
        int infd = open(argv[1], O_RDONLY);
        if (infd == -1)
            err_exit("file O_RDONLY error");
        int outfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666);
        if (outfd == -1)
            err_exit("file O_WRONLY error");
    
        char buf[1024];
        int readBytes, writeBytes;
        while ((readBytes = read(infd, buf, sizeof(buf))) > 0)
        {
            writeBytes = write(outfd, buf, readBytes);
            cout << "readBytes = " << readBytes
                 << ", writeBytes = " << writeBytes << endl;
        }
    }

    文件的随机读写

    5.lseek

    对应于C库函数中的fseek, 通过指定相对于当前位置, 末尾位置或开始位置的字节数来重定位currp:

    off_t lseek(int fd, off_t offset, int whence);

    返回值: 新的文件偏移值;

     

    Whence取值:

    SEEK_SET

       The offset is set to offset bytes.

    SEEK_CUR

       The offset is set to its current location plus offset bytes.

    SEEK_END

       The offset is set to the size of the file plus offset bytes.

    //示例1
    int main(int argc, char *argv[])
    {
        int fd = open("test.txt", O_RDONLY);
        if (fd == -1)
            err_exit("open error");
        char buf[1024] = {0};
        int readBytes = read(fd, buf, 5);
        cout << "readBytes = " << readBytes << ", buf: " << buf << endl;
        int seekCount = lseek(fd, 0, SEEK_CUR);
        cout << "current offset = " << seekCount << endl;
    }
    //示例2: 产生空洞文件
    int main(int argc, char *argv[])
    {
        int fd = open("hole.txt", O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666);
        if (fd == -1)
            err_exit("open error");
    
        if (write(fd, "ABCDE", 5) == -1)
            err_exit("first write error");
        //创建一个1G的文件
        if (lseek(fd, 1024*1024*1024, SEEK_CUR) == -1)
            err_exit("lseek error");
        if (write(fd, "Hello", 5) == -1)
            err_exit("second write error");
        close(fd);
    }

    [附]

    -查看hole.txt文件

      od -c hole.txt

      cat -A hole.txt

    -查看该文件大小

      du -h hole.txt

      du -b hole.txt

      du -k hole.txt

      du -m hole.txt

     

    目录访问

    6.opendir

           #include <sys/types.h>
           #include <dirent.h>
           DIR *opendir(const char *name);

    返回值:

       成功: 返回目录指针;

       失败: 返回NULL;

     

    7.readdir

    struct dirent *readdir(DIR *dirp);

    返回值:

       成功: 返回一个指向dirent结构的指针, 它包含指定目录的下一个连接的细节;

       没有更多连接时, 返回0;

    struct dirent
    {
        ino_t          d_ino;       /* inode number */
        off_t          d_off;       /* not an offset; see NOTES */
        unsigned short d_reclen;    /* length of this record */
        unsigned char  d_type;      /* type of file; not supported
                                             by all filesystem types */
        char           d_name[256]; /* filename */
    };

    8.closedir: 关闭目录   

    int closedir(DIR *dirp);
    //示例: 简单的ls程序
    int main(int argc, char *argv[])
    {
        if (argc < 2)
        {
            cerr << "Usage : " << argv[0] << " <directory>" << endl;
            exit(EXIT_FAILURE);
        }
    
        DIR *dir = opendir(argv[1]);
        if (dir == NULL)
            err_exit("opendir error");
    
        struct dirent *ent;
        while ((ent = readdir(dir)) != NULL)
        {
            //过滤掉隐藏文件
            if (ent->d_name[0] == '.')
                continue;
            cout << ent->d_name << "	i-node: " << ent->d_ino
                 << ", length: " << ent->d_reclen << endl;
        }
        closedir(dir);
    }

    9.mkdir

    int mkdir(const char *pathname, mode_t mode);

    10.rmdir: 删除空目录

    int rmdir(const char *pathname);

    11. Chmod, fchmod更改权限

    int chmod(const char *path, mode_t mode);
    int fchmod(int fd, mode_t mode);

    12.chown,fchown更改文件所有者/所属组

    int chown(const char *path, uid_t owner, gid_t group);
    int fchown(int fd, uid_t owner, gid_t group);

  • 相关阅读:
    P2617 Dynamic Rankings (动态开点权值线段树 + 树状数组)
    2019HDU多校 Round5
    2019牛客多校 Round6
    2019牛客多校 Round5
    2019HDU多校 Round4
    [转]SQLServer : EXEC和sp_executesql的区别
    无法执行 FunctionImport“entitys.xx”,因为未将它映射到存储函数。EF
    【终极解决方案】为应用程序池“XXX”提供服务的进程在与 Windows Process Activation Service 通信时出现严重错误。该进程 ID 为“XXXX”。数据字段包含错误号。
    IE11 Windows7下F12 DOC资源管理器不能用Exception in window.onload: Error: An error has ocurredJSPlugin.3005
    VS2015编译错误:调用的目标发生了异常--->此实现不是Windows平台FLPS验证的加密算法的一部分。
  • 原文地址:https://www.cnblogs.com/itrena/p/5926979.html
Copyright © 2020-2023  润新知