zoukankan      html  css  js  c++  java
  • 文件I/O操作

    本文转自实验楼:https://www.shiyanlou.com/courses/24

    一.linux I/O介绍

        Linux下一切皆文件,因此文件操作属于Linux下的基本操作。对于用户层来说,所有的操作都是通过一系列的系统调用来修改Linux系统中文件的操作。

        Linux提供了一系列系统调用接口(API),遵循了UNIX中最流行的应用编程界面标准——POSIX。这些系统调用编程接口主要是通过C库(libc)实现的。

     二.文件描述符

        对内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当写一个文件时,用open或create返回的文件描述符标识该文件,将其作为参数传送给read或write。

        在POSIX应用程序中,整数0、1、2应被代换成符号常数:

    1. STDIN_FILENO(标准输入,默认是键盘)
    2. STDOUT_FILENO(标准输出,默认是屏幕)
    3. STDERR_FILENO(标准错误输出,默认是屏幕)

        这些常数都定义在头文件<unistd.h>中,文件描述符的范围是0~OPEN_MAX。早期的UNIX版本采用的上限值是19(允许每个进程打开20个文件), 现在很多系统则将其增加至256。

        可用的文件IO函数很多,包括:打开文件,读文件,写文件等。大多数Linux文件IO只需要用到5个函数:open,read,write,lseek以及close。

    三、文件操作相关API

    1. open

        需要包含的头文件:<sys/types.h>, <sys/stat.h>, <fcntl.h>

        函数原型:

    int open(const str * pathname, int oflag, [..., mode_t mode])

        功能:打开文件

        返回值:成功则返回文件描述符,出错返回-1

        参数:

        pathname: 打开或创建的文件的全路径名

        oflag:可用来说明此函数的多个选择项, 详见后。

        mode:对于open函数而言,仅当创建新文件时才使用第三个参数,表示新建文件的权限设置。

    详解oflag参数:

    oflag 参数由O_RDONLY(只读打开)、O_WRONLY(只写打开)、O_RDWR(读写打开)中的一个于下列一个或多个常数

    O_APPEND: 追加到文件尾

    O_CREAT: 若文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明新闻件的访问权限

    O_EXCL: 如果同时指定O_CREAT,而该文件又是存在的,报错;也可以测试一个文件是否存在,不存在则创建。

    O_TRUNC: 如果次文件存在,而且为读写或只写成功打开,则将其长度截短为0

    O_SYNC: 使每次write都等到物理IO操作完成

    用open创建一个文件: open.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define FILE_PATH   "./test.txt"
    
    int main(void)
    {
        int fd;
        if ((fd = open(FILE_PATH, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
            printf("open error
    ");
            exit(-1);
        } else {
            printf("open success
    ");
        }
        return 0;
    }

        如果当前目录下以存在test.txt,屏幕上就会打印“open error”;不存在则创建该文件,并打印“open success”

    2. read

        需要包含的头文件:<unistd.h>

        函数原型:

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

        功能:从打开的文件中读取数据。

        返回值:实际读到的字节数;已读到文件尾返回0,出错的话返回-1,ssize_t是系统头文件中用typedef定义的数据类型相当于signed int

        参数:

        fd:要读取的文件的描述符

        buf:得到的数据在内存中的位置的首地址

        count:期望本次能读取到的最大字节数。size_t是系统头文件中用typedef定义的数据类型,相当于unsigned int

    3. write

        需要包含的头文件:<unistd.h>

        函数原型:

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

        功能:向打开的文件写数据

        返回值:写入成功返回实际写入的字节数,出错返回-1

    不得不提的是,返回-1的常见原因是:磁盘空间已满,超过了一个给定进程的文件长度

        参数:

        fd:要写入文件的文件描述符

        buf:要写入文件的数据在内存中存放位置的首地址

        count:期望写入的数据的最大字节数

    read和write使用范例

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(void)
    {
        char buf[100];
        int num = 0;
    
        // 获取键盘输入,还记得POSIX的文件描述符吗?
        if ((num = read(STDIN_FILENO, buf, 10)) == -1) {
            printf ("read error");
            error(-1);
        } else {
        // 将键盘输入又输出到屏幕上
            write(STDOUT_FILENO, buf, num);
        }
    
        return 0;
    }

    4. close

        需要包含的头文件:unistd.h

        函数原型:

    int close(int filedes)

        功能:关闭一个打开的文件

        参数:需要关闭文件的文件描述符

    当一个进程终止的时候,它所有的打开文件都是由内核自动关闭。很多程序都使用这一功能而不显式地调用close关闭一个已打开的文件。

    但是,作为一名优秀的程序员,应该显式的调用close来关闭已不再使用的文件。

    5. lseek

        每个打开的文件都有一个“当前文件偏移量”,是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作都是从当前文件偏移量处开始,并使偏移量增加所读或写的字节数。默认情况下,你打开一个文件时(open),除非指定O_APPEND参数,不然位移量被设为0。

        需要包含的头文件:sys/types.h, unistd.h

        函数原型:

    off_t lseek(int filesdes, off_t offset, int whence)

        功能:设置文件内容读写位置

        返回值:成功返回新的文件位移,出错返回-1;同样off_t是系统头文件定义的数据类型,相当于signed int

        参数:

    1. whence是SEEK_SET, 那么该文件的位移量设置为据文件开始处offset个字节
    2. whence是SEEK_CUR, 那么该文件的位移量设置为当前值加offset。offset可为正或负
    3. whence是SEEK_END, 那么该文件的位移量设置为文件长度加offset。offset可为正或负
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main(int argc, char * argv[])
    {
        int fd;
        char buf[100];
        if ((fd = open(argv[1], O_RDONLY)) < 0) {
            perror("open");
            exit(-1);
        }
        read(fd, buf, 1);
        write(STDOUT_FILENO, buf, 1);
        lseek(fd, 2, SEEK_CUR);
    
        read(fd, buf, 1);
        write(STDOUT_FILENO, buf, 1);
        lseek(fd, -1, SEEK_END);
    
        read(fd, buf, 1);
        write(STDOUT_FILENO, buf, 1);
        lseek(fd, 0, SEEK_SET);
    
        read(fd, buf, 1);
        write(STDOUT_FILENO, buf, 1);
        close(fd);
        printf("
    ");
    
        return 0;
    }

    四、文件属性查看API——stat

        Linux下每个文件都有他自己的属性,如:文件的类型、操作权限、硬链接数量、属主、所属组、大小、修改时间、文件名。在shell模式下这些属性可以通过 ls -l命令来进行查看,但是在编程模式下就需要相关API进行操作——stat。

    1. stat 的基本使用

        系统调用stat的作用是获取文件的各个属性。

        需要包含的头文件:<sys/types.h><sys/stat.h><unistd.h>

        函数原型:

    int stat(const char * path, struct stat * buf)

        功能:查看文件或目录属性。将参数path所指的文件的属性,复制到参数buf所指的结构中。

        参数:

        path:要查看属性的文件或目录的全路径名称。

        buf:指向用于存放属性的结构体。stat成功调用后,buf的各个字段将存放各个属性。struct stat是系统头文件中定义的结构体,定义如下:

    struct stat {
        dev_t       st_dev;
        ino_t       st_ino;
        mode_t      st_mode;
        nlink_t     st_nlink;
        uid_t       st_uid;
        gid_t       st_gid;
        dev_t       st_rdev;
        off_t       st_size;
        blksize_t   st_blksize;
        blkcnt_t    st_blocks;
        time_t      st_atime;
        time_t      st_mtime;
        time_t      st_ctime;
    };

        st_ino:节点号

        st_mode:文件类型和文件访问权限被编码在该字段中

        st_nlink:硬连接数

        st_uid:属主的用户ID

        st_gid:所属组的组ID

        st_rdev:设备文件的主、次设备号编码在该字段中

        st_size:文件的大小

        st_mtime:文件最后被修改时间

        返回值:成功返回0;失败返回-1

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(int argc, char **argv)
    {
        struct stat     buf;
        if(argc != 2) { 
            printf("Usage: stat <pathname>"); 
            exit(-1); 
        }
        if(stat(argv[1], &buf) != 0) { 
            printf("stat error."); 
            exit(-1); 
        }
        printf("#i-node:    %ld
    ", buf.st_ino);
        printf("#link:      %d
    ", buf.st_nlink);
        printf("UID:        %d
    ", buf.st_uid);
        printf("GID:        %d
    ", buf.st_gid);
        printf("Size        %ld
    ", buf.st_size);
        exit(0);
    }

    2. 文件类型的判定

        上一小节中struct stat中有个字段为st_mode,可用来获取文件类型和文件访问权限,我们将陆续学到从该字段解码我们需要的文件信息。

        st_mode中文件类型宏定义

    宏定义文件类型
    S_ISREG() 普通文件
    S_ISDIR() 目录文件
    S_ISCHR() 字符设备文件
    S_ISBLK() 块设备文件
    S_ISFIFO() 有名管道文件
    S_ISLNK() 软连接(符号链接)文件
    S_ISSOCK() 套接字文件

    我们修改上面的例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(int argc, char **argv)
    {
        struct stat buf;
        char * file_mode;
        if(argc != 2) {
            printf("Usage: stat <pathname>
    "); 
            exit(-1); 
        }
        if(stat(argv[1], &buf) != 0) {
            printf("stat error.
    "); 
            exit(-1); 
        }
        if (S_ISREG(buf.st_mode))
            file_mode = "-";
        else if (S_ISDIR(buf.st_mode))
            file_mode = "d";
        else if (S_ISCHR(buf.st_mode))
            file_mode = "c";
        else if(S_ISBLK(buf.st_mode))
            file_mode = "b";
        printf("#i-node:    %ld
    ", buf.st_ino);
        printf("#link:      %d
    ", buf.st_nlink);
        printf("UID:        %d
    ", buf.st_uid);
        printf("GID:        %d
    ", buf.st_gid);
        printf("Size        %ld
    ", buf.st_size);
        printf("mode: %s
    ", file_mode);
        exit(0);
    }

     3. 文件权限的判定

        文件类型与许可设定被一起编码在st_mode字段中,同上面一样,我们也需要一组由系统提供的宏来完成解码。

    宏定义文件类型
    S_ISUID 执行时,设置用户ID
    S_ISGID 执行时,设置组ID
    S_ISVTX 保存正文
    S_IRWXU 拥有者的读、写和执行权限
    S_IRUSR 拥有者的读权限
    S_IWUSR 拥有者的写权限
    S_IXUSR 拥有者的执行权限
    S_IRWXG 用户组的读、写和执行权限
    S_IRGRP 用户组的读权限
    S_IWGRP 用户组的写权限
    S_IXGRP 用户组的执行权限
    S_IRWXO 其它读、写、执行权限
    S_IROTH 其它读权限
    S_IWOTH 其它写权限
    S_IXOTH 其它执行权限

    五、 目录操作

        当目标是目录而不是文件的时候,ls -l的结果会显示目录下所有子条目的信息,怎么去遍历整个目录呢?答案马上揭晓!

    1. 打开目录

        需要包含的头文件:<sys/types.h><dirent.h>

        函数原型:

    DIR * opendir(const char * name)

        功能:opendir()用来打开参数name指定的目录,并返回DIR *形态的目录流

        返回值:成功返回目录流;失败返回NULL

    2. 读取目录

        函数原型:struct dirent * readdir(DIR * dir)

        功能:readdir()返回参数dir目录流的下一个子条目(子目录或子文件)

        返回值: 成功返回结构体指向的指针,错误或以读完目录,返回NULL

    函数执行成功返回的结构体原型如下:

    struct dirent {
       ino_t   d_ino;
       off_t   d_off;
       unsigned short  d_reclen;
       unsigned char   d_type;
       char    d_name[256];
    };
    

    其中 d_name字段,是存放子条目的名称

    3. 关闭目录

        函数原型:

    int closedir(DIR * dir)

        功能:closedir()关闭dir所指的目录流

        返回值:成功返回0;失败返回-1,错误原因在errno中

        我们来学习一个综合的例子吧:

    #include <stdio.h>
    #include <stdlib.h>
    #include <dirent.h>
    int main(int argc, char *argv[])
    {
        DIR *dp;
        struct dirent *entp;
        if (argc != 2) {
            printf("usage: showdir dirname
    ");
            exit(0);
        }
        if ((dp = opendir(argv[1])) == NULL) {
            perror("opendir");
            exit(-1);
        }
        while ((entp = readdir(dp)) != NULL)
            printf("%s
    ", entp->d_name);
    
        closedir(dp);
        return 0;
    }
  • 相关阅读:
    Ext.dataGroupingStore/JsonStore/SimpleStore
    转:LinQ操作汇总(From CSharpSamples)
    XSLT教程 比较全的
    使用ASP.Net Forms模式实现WebService身份验证
    关于DataRow的RowState和RowVersion
    C#日志工具汇总
    转 Using log4net,
    js//初始话日期
    两个数据库表的连接 查询
    ExtJS入门之三 查询
  • 原文地址:https://www.cnblogs.com/edver/p/7473494.html
Copyright © 2011-2022 走看看