• 文件I/O


    文件I/O

    一、先来了解下什么是文件I/O和标准I/O:

    文件I/O:文件I/O称之为不带缓存的IO(unbuffered I/O)。不带缓存指的是每个read,write都调用内核中的一个系统调用。也就是一般所说的低级I/O——操作系统提供的基本IO服务,与os绑定,特定于linix或unix平台。

    标准I/O:标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性。标准I/O库处理很多细节。例如缓存分配,以优化长度执行I/O等。标准的I/O提供了三种类型的缓存。

    (1)全缓存:当填满标准I/O缓存后才进行实际的I/O操作。 
    (2)行缓存:当输入或输出中遇到新行符时,标准I/O库执行I/O操作。 
    (3)不带缓存:stderr就是了。

    二、二者的区别

          文件I/O 又称为低级磁盘I/O,遵循POSIX相关标准。任何兼容POSIX标准的操作系统上都支持文件I/O。标准I/O被称为高级磁盘I/O,遵循ANSI C相关标准。只要开发环境中有标准I/O库,标准I/O就可以使用。(Linux 中使用的是GLIBC,它是标准C库的超集。不仅包含ANSI C中定义的函数,还包括POSIX标准中定义的函数。因此,Linux 下既可以使用标准I/O,也可以使用文件I/O)。

          通过文件I/O读写文件时,每次操作都会执行相关系统调用。这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销,标准I/O可以看成是在文件I/O的基础上封装了缓冲机制。先读写缓冲区,必要时再访问实际文件,从而减少了系统调用的次数。

          文件I/O中用文件描述符表现一个打开的文件,可以访问不同类型的文件如普通文件、设备文件和管道文件等。而标准I/O中用FILE(流)表示一个打开的文件,通常只用来访问普通文件。
    ————————————————
    版权声明:上面内容为CSDN博主「zqixiao_09」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zqixiao_09/article/details/50444465

    三、文件I/O常见函数

    (1)open函数:打开文件

    参数介绍: 

    *pathname:文件路径和文件名

    flags:标志位,用来指定打开的模式,O_RDONLY(只读),O_WRONLY(只写),O_RDWR (可读可写),还有以下几个参数

    O_APPEND: 追加到文件尾。

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

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

    O_TRUNC: 如果文件存在,而且为读写或只写成功打开,则将其长度截短为0,与O_APPEND相对,若文件存在将会把内容清空。

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

    mode:只有在文件创建才用到,用于指定文件访问权限:

    参数 说明 参数 说明
    S_IRUSR

    所有者拥有

    读权限

    S_IXGRP

    群组拥有执

    行权限

    S_IWUSR

    所有者拥有

    写权限

    S_IROTH

    其他用户拥

    有读权限

    S_IXUSR

    所有者拥有

    执行权限

    S_IWOTH

    其他用户拥

    有写权限

    S_IRGRP 群组拥有读权限 S_IXOTH
     

    其他用户拥

    有执行权限

     S_IWGRP  群组拥有写权限    

    文件权限标志也可以使用加权数字表示,这组数字被称为umask变量,它的类型是mode_t,是一个无符号八进制数。umask变量的定义方法如表13.4所示。umask变量由3位数字组成,数字的每一位代表一类权限。用户所获得的权限是加权数值的总和。例如764表示所有者拥有读、写和执行权限,群组拥有读和写权限,其他用户拥有读权限。

    open函数的返回值:

    1、返回值是一个整数。

    2、打开文件成功,返回文件描述符。

    3、打开文件失败,返回-1。

    我们用一段代码来演示函数如何使用,从上面的截图中我们可以看到open函数有两种情况。

    情况一:

    1 #include <sys/types.h>
    2 #include <sys/stat.h>
    3 #include <fcntl.h>
    4 
    5 int open(const char *pathname, int flags);

    首先我们创建一个名为file.txt的文件,接下来我们就打开这个文件。

     代码如下:

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd=open("file.txt",O_RDONLY);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("fd=%d
    ",fd);
    15     printf("Open successfull
    ");
    16     return 0;
    17 }

    运行结果

     这是文件存在的情况,文件可以正常打开,如果文件不存在,将会用perror输出错误信息

     如果文件不存在,我们就需要用到标志位的O_CREAT。

    代码如下:

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd=open("file.txt",O_RDONLY|O_CREAT);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("fd=%d
    ",fd);
    15     printf("Open successfull
    ");
    16     return 0;
    17 }

    运行结果:

     可以通过终端发现,刚开始在文件夹下并没有file.txt,运行程序之后新建了一个file.txt,但是我们想,Linux下的文件都是有权限的,这样才能更好的控制这个文件,这里就需要open函数的第二种用法了。

    int open(const char *pathname, int flags, mode_t mode);

     这是我们没有设置权限的情况下,系统默认加的,当我们使用设置权限时

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd=open("file.txt",O_RDONLY|O_CREAT,0664);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("fd=%d
    ",fd);
    15     printf("Open successfull
    ");
    16     return 0;
    17 }

    运行结果:

     可以看到文件已经设置成了我们想要的权限。

     (2)close函数:关闭文件

     该函数只有一个参数,就是文件的描述符,返回值是整型,如果返回0就表示关闭成功,返回-1就表示失败。

    代码:

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd=open("file.txt",O_RDONLY|O_CREAT,0664);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("fd=%d
    ",fd);
    15     printf("Open successfull
    ");
    16     int flag;
    17     if((flag = close(fd)) == 0)
    18     {
    19         printf("flag=%d
    ",flag);
    20         printf("Close successful
    ");
    21     }
    22     return 0;
    23 }

    运行结果:

     我们需要注意一点,如果我们文件操作完之后一定要记得关闭文件,否则可能会造成文件损坏。

    (3)write函数:向文件中写内容

     参数介绍:

    fd:文件描述符

    buf:要写入文件的内容

    count:要写入文件的内容的长度,以字节为单位

    返回值:写入成功则返回写入的字节数,失败则返回-1

    代码演示:

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd = open("file.txt",O_WRONLY|O_CREAT,0664);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("Open successful
    ");
    15     char buf[] = "hello world";
    16     if(write(fd,buf,11) == -1)
    17     {
    18         perror("Fail to write:");
    19         close(fd);
    20         return -1;
    21     }
    22     printf("Write successful
    ");
    23     close(fd);
    24     return 0;
    25 }

    运行结果:

     这里我们要提一下open函数的两个flag——O_TRUNC和O_APPEND,前者表示清除文件的内容,后者表示在文件原有内容的后面加上,我们用代码来演示一下。

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd = open("file.txt",O_WRONLY|O_CREAT,0664);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("Open successful
    ");
    15     char buf[] = "hello world";
    16     if(write(fd,buf,11) == -1)
    17     {
    18         perror("Fail to write:");
    19         close(fd);
    20         return -1;
    21     }
    22     printf("file.txt中写入的内容是:%s
    ",buf);
    23     close(fd);
    24     fd = open("file.txt",O_WRONLY|O_TRUNC);
    25     if(fd == -1)
    26     {
    27         perror("Fail to open:");
    28         return -1;
    29     }
    30     char buf2[] = "welcome to ChangSha";
    31     if(write(fd,buf2,19) == -1)
    32     {
    33         perror("Fail to write:");
    34         close(fd);
    35         return -1;
    36     }
    37     printf("file.txt中又写入了%s
    ",buf2);
    38     close(fd);
    39     return 0;
    40 }

    运行结果:

    在这段代码中我们用得是O_TRUNC,我们先写入了hello world,然后又写入了welcome to ChangSha,但最后我们发现只剩第二句了,那是因为第一次写得被清空了。接下来让我们再用一下O_APPEND来看一下区别。

    代码:

     1 #include<sys/types.h>
     2 #include<sys/stat.h>
     3 #include<unistd.h>
     4 #include<stdlib.h>
     5 #include<stdio.h>
     6 #include<fcntl.h>
     7 int main() {
     8     int fd = open("file.txt",O_WRONLY|O_CREAT,0664);
     9     if(fd == -1)
    10     {
    11         perror("Fail to open:");
    12         return -1;
    13     }
    14     printf("Open successful
    ");
    15     char buf[] = "hello world";
    16     if(write(fd,buf,11) == -1)
    17     {
    18         perror("Fail to write:");
    19         close(fd);
    20         return -1;
    21     }
    22     printf("file.txt中写入的内容是:%s
    ",buf);
    23     close(fd);
    24     fd = open("file.txt",O_WRONLY|O_APPEND);
    25     if(fd == -1)
    26     {
    27         perror("Fail to open:");
    28         return -1;
    29     }
    30     char buf2[] = "welcome";
    31     if(write(fd,buf2,7) == -1)
    32     {
    33         perror("Fail to write:");
    34         close(fd);
    35         return -1;
    36     }
    37     printf("file.txt中又写入了%s
    ",buf2);
    38     close(fd);
    39     return 0;
    40 }

    运行结果:

     可以看到在hello world后面又加入了welcome,这就是O_APPEND的效果。

     (4)read函数:读取文件中内容

    参数介绍:

      fd:文件描述符

      buf:读取到的内容存放的位置

      count:读取到的字节数

    返回值:

      成功:有两种情况,一种是返回读取到的字节数,一种是0(注意:返回0并不是就表示出错了,而是我们读取到了文件末尾)

      失败:返回-1,并且可通过perror打印错误信息。

    代码演示:

    我们首先创建一个文件,在文件中写入hello linux,接下来将用read函数读取这个文件的内容。

     代码演示:

     1 #include<sys/types.h>
     2 #include<unistd.h>
     3 #include<stdio.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<sys/stat.h>
     7 int main(int argc, char const *argv[])
     8 {
     9     int fd = open("test.txt",O_RDONLY);
    10     if(fd == -1)
    11     {
    12         perror("Fail to open:");
    13         exit(2);
    14     }
    15     char buf[128]="";
    16     ssize_t byte;
    17     if((byte = read(fd,buf,sizeof(buf))) == -1)
    18     {
    19         perror("Fail to read");
    20         exit(2);
    21     }
    22     printf("the number of bytes read is %ld,%s
    ",byte,buf);
    23     close(fd);
    24     return 0;
    25 }

    输出结果:

     我们在看下返回值为0的情况,我们读取的文件还是同上个示例一样,这次有所不同的就是当我们读取了一次内容之后再读一次,很明显,buf的长度大于文件内容,所以第二次我们就要读到文件的结尾了,这次我们再看read的返回值。

     1 #include<sys/types.h>
     2 #include<unistd.h>
     3 #include<stdio.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<sys/stat.h>
     7 #include<string.h>
     8 int main(int argc, char const *argv[])
     9 {
    10     int fd = open("test.txt",O_RDONLY);
    11     if(fd == -1)
    12     {
    13         perror("Fail to open:");
    14         exit(2);
    15     }
    16     char buf[128]="";
    17     ssize_t byte;
    18     if((byte = read(fd,buf,sizeof(buf))) == -1)
    19     {
    20         perror("Fail to read");
    21         exit(2);
    22     }
    23     printf("the number of bytes read is %ld,%s
    ",byte,buf);
    24     memset(buf,0,sizeof(buf));
    25     if((byte = read(fd,buf,sizeof(buf))) == -1)
    26     {
    27         perror("Fail to read");
    28         exit(2);
    29     }
    30     printf("the number of bytes read is %ld,%s
    ",byte,buf);
    31     close(fd);
    32     return 0;
    33 }

    运行结果:

     那么为什么会这样呢?这里我们就需要提到一个概念,就是文件指针,刚开始文件指针的位置是在文件开头,当我们读取文件文件内容后指针就会移动到我们读取到的位置,因此当我们在读时,就会使文件结尾,那我们如果想继续读,就要修改文件指针的位置,这时候就要用到lseek函数。

    (5)lseek函数:修改文件指针的位置

     参数介绍:

      fd:文件描述符

      offset:基于whence的偏移量

      whence:有三个值

        ①SEEK_SET:文件开头位置

        ②SEEK_CUR:文件指针当前位置

        ③SEEK_END:文件末尾

    返回值:

      文件读写指针距文件开头的字节大小,出错,返回-1

      lsee的作用是打开文件下一次读写的开始位置,因此还有以下两个作用:

          1.拓展文件,不过一定要一次写的操作。迅雷等下载工具在下载文件时候先扩展一个空间,然后再下载的。

          2.获取文件大小。

    接下来的示例基于上面那个示例,让我们修改文件指针的位置然后再读。

     1 #include<sys/types.h>
     2 #include<unistd.h>
     3 #include<stdio.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<sys/stat.h>
     7 #include<string.h>
     8 int main(int argc, char const *argv[])
     9 {
    10     int fd = open("test.txt",O_RDONLY);
    11     if(fd == -1)
    12     {
    13         perror("Fail to open:");
    14         exit(2);
    15     }
    16     char buf[128]="";
    17     ssize_t byte;
    18     if((byte = read(fd,buf,sizeof(buf))) == -1)
    19     {
    20         perror("Fail to read");
    21         exit(2);
    22     }
    23     printf("the number of bytes read is %ld,%s
    ",byte,buf);
    24     lseek(fd,0,SEEK_SET);
    25     memset(buf,0,sizeof(buf));
    26     if((byte = read(fd,buf,sizeof(buf))) == -1)
    27     {
    28         perror("Fail to read");
    29         exit(2);
    30     }
    31     printf("the number of bytes read is %ld,%s
    ",byte,buf);
    32     close(fd);
    33     return 0;
    34 }

    运行结果:

     (四)案例实践

    案例一:用lseek函数获取文件长度

    首先我们先看下我们事先准备好的一个文本文件,看一下它的大小

     我们可以看到文件的大小为70

     1 #include<sys/types.h>
     2 #include<unistd.h>
     3 #include<stdio.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<sys/stat.h>
     7 #include<string.h>
     8 int main(int argc, char const *argv[])
     9 {
    10     int fd;
    11     fd = open(argv[1],O_RDONLY);
    12     if(fd < 0)
    13     {
    14         perror("open:");
    15         exit(1);
    16     }
    17     int ret = lseek(fd,0,SEEK_END);
    18     printf("the file size is:%d
    ",ret);
    19     close(fd);
    20     return 0;
    21 }

    运行结果:

     案例二:lseek拓展文件大小

    我们基于上面的示例文件,我们把它的大小拓展到100,拓展的时候要注意,必须要进行一次写的操作,我们要首先让文件指针偏移一定的位置,然后写点内容,文件拓展后的大小应当等于偏移量加写入内容的大小,比如说我们文件原本大小70,要拓展到100,并且要写入的内容是"hello",我们就先便宜25个,再写入"hello",大小就变成100了。

     1 #include<sys/types.h>
     2 #include<unistd.h>
     3 #include<stdio.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<sys/stat.h>
     7 #include<string.h>
     8 int main(int argc, char const *argv[])
     9 {
    10     int fd;
    11     fd = open(argv[1],O_RDWR);
    12     if(fd < 0)
    13     {
    14         perror("open");
    15         exit(1);
    16     }
    17     int ret; 
    18     if((ret =  lseek(fd,0,SEEK_END)) == -1)
    19     {
    20         perror("lseek");
    21         exit(1);
    22     }
    23     printf("befork expand,the file size is:%d
    ",ret);
    24     if((ret =  lseek(fd,25,SEEK_END)) == -1)
    25     {
    26         perror("lseek");
    27         exit(1);
    28     }
    29     if(write(fd,"hello",5) == -1)
    30     {
    31         perror("write");
    32         exit(1);
    33     }
    34     if((ret =  lseek(fd,0,SEEK_END)) == -1)
    35     {
    36         perror("lseek");
    37         exit(1);
    38     }
    39     printf("after expand,the file size is:%d
    ",ret);
    40     close(fd);
    41     return 0;
    42 }

    运行结果:

     

     案例三:write和read实现cp命令

     首先我们创建两个文件,一个文件名叫souce.txt,里面存有我们要复制的内容,另外那个文件叫souce_cp.txt,内容为空,将souce.txt中的内容复制到里面。

     1 #include<sys/types.h>
     2 #include<unistd.h>
     3 #include<stdio.h>
     4 #include<fcntl.h>
     5 #include<stdlib.h>
     6 #include<sys/stat.h>
     7 #include<string.h>
     8 int main(int argc, char const *argv[])
     9 {
    10     int fd1,fd2;
    11     char buf[1024] = "";
    12     if((fd1 = open(argv[1],O_RDWR)) < 0)
    13     {
    14         perror("open");
    15         exit(1);
    16     }
    17     if((fd2 = open(argv[2],O_RDWR)) < 0)
    18     {
    19         perror("open");
    20         exit(1);
    21     }
    22     while ((read(fd1,buf,sizeof(buf))) > 0)
    23     {
    24         if(write(fd2,buf,sizeof(buf)) < 0)
    25         {
    26             perror("write");
    27             exit(1);
    28         }
    29         memset(buf,0,sizeof(buf));
    30     }
    31     printf("cp successed!
    ");
    32     close(fd1);
    33     close(fd2);
    34     return 0;
    35 }

    运行结果:

     

     

     两个文件的内容一模一样。

    以上是本次内容,大家有什么不懂的可以和我交流,或者我有写错的地方,欢迎大家指正,Q:1033278524。

  • 相关阅读:
    错误:Char 26: fatal error: reference to non-static member function must be called
    【C++】去除字符串string中的空格(两头空格、所有空格)
    【C/C++】字符串string与字符数组char*的相互转换
    【C++】if-else编程陷阱
    【数据结构与算法】《剑指offer》学习笔记----第一章、第二章 基础知识(含1-15题)
    LeetCode运行报错: reference binding to null pointer of type 'value_type'
    【深度学习机配置】Dell服务站各组件型号记录
    【C++、二分法】LeetCode744. 寻找比目标字母大的最小字母
    Python视频抽帧成图片
    Windows10自带的录屏软件,十分强大
  • 原文地址:https://www.cnblogs.com/953-zjf/p/14507470.html
Copyright © 2020-2023  润新知