文件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。

原文地址:https://www.cnblogs.com/953-zjf/p/14507470.html