文件I/O

原创 2015年11月18日 13:31:49

文件 I/O


1.什么是文件I/O?

简单的说文件I/O就是对文件的读写,但由于要考虑到linux支持多进程多线程、文件权限、读写效率等诸多因素,简单的读写操作也变得颇有讲究。

2.文件描述符

文件描述符就是一个文件的身份证,通过它系统才可以对文件进行操作。文件描述符是一个非负整数,当打开一个已有的文件或者创建一个新文件时,内核回想进城返回一个文件描述符,当要读写一个文件时,需要将文件描述符传递给相应的读写函数。有三个文件描述符是系统shell默认的:0-标准输入;1-标准输出;2-标准错误。由于这些数字缺乏可读性,系统设置了三个常量与之对应:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。

3.关于文件读写的一些函数

open或openat:打开或创建一个文件

函数原型

  1. #include<fcntl.h>
  2. int open(const char* pathname, int flags, ...../*mode_t mode*/)
  3. int openat( int fd ,const char* pathname , int flags ,...../*mode_t mode*/)

参数解释 
pathname:打开或创建文件的路径,当pathname是绝对路径时open和openat两个函数是等效的,openat的参数fd被忽略。当pathname是相对路径时,open()函数是在当前目录下打开或创建一个文件,而openat函数则避免了这样的缺陷,它可以通过指定fd,在其他目录下打开或创建文件。fd参数指定相对路径所在的起始地址,比如如果要在/etc/目录下打开或创建文件,fd可以通过打开/etc/目录来获取。这样新打开或创建的文件就可以在/etc/目录下而不是当前目录。 
flags:这个参数主要是指定打开的方式,系统定义的一些常量用来描述这些方式这里给出一部分常量以及它的解释,完整的内容可通过man手册查询。 
O_RDONLY:以只读的方式打开; 
O_WRONLY:以只写的方式打开; 
O_RDWR:以读写的方式打开; 
O_EXEC:以只执行的方式打开; 
O_SEARCH:以搜索的方式打开(应用于目录) 
以上五种方式是互斥的,这五种方式必须选一种且只能选一种。 
下面还有一些常量是可选的 
O_APPEND:每次写时追加到文件尾部; 
O_CLOEXEC:把FD_CLOEXEC设置为文件描述符标志,在下面介绍dup函数时有应用; 
O_CREAT:若文件不存在则创建,当flags中使用此方式,就得说明第三个参数mode,mode设置了新创建文件的访问权限,系统也为文件的访问权限定义了一些常量。 
返回值 
open和openat函数返回文件描述符,该描述符是系统最小且尚未使用的描述符数值。若失败则返回-1.

creat函数:创建一个新文件。

函数原型

  1. #include<fcntl.h>
  2. int creat(char* pathname,mode_t mode);

参数解释与open函数一样。 
其实完全可以用open实现creat函数,并且更加灵活,creat相当于

  1. int open (char* pathname,O_WRONLY|O_CREAT|O_TRUNC,mode);

creat函数有一个缺陷,就是它以只写的方式创建文件,若想对新创建的文件进行读操作那么必须先close文件,再以可读的方式打开,可用open函数避免这个问题

  1. int open(char* pathname, O_RDWR|O_CREAT|O_TRUNC,mode);

返回值 
成功则返回文件描述符,若失败返回-1.

lseek函数:为一个已打开的文件设置偏移量。

当对文件进行读写操作时,需要一个读写的起始地址,这个起始地址就是文件偏移量,读写操作都是从文件偏移量处开始。偏移量会随着读写的进行改变,读写几个字节它就增加几个字节。当打开一个文件除非指定以O_APPEND的方式,否则文件偏移量被设为0。lseek函数可以显示地设置文件偏移量,这样读写文件时程序将更具灵活性。 
函数原型

  1. #include<unistd.h>
  2. off_t lseek(int fd,off_t offset,int whence);

参数说明 
fd:文件描述符; 
offset:偏移量; 
whence:参考位置。 
当whence为SEEK_SET时,文件偏移量设置为距文件开始处offset个字节; 
当whence为SEEK_END时,文件偏移量设置为文件长度加上offset,offset可正可负; 
当whence为SEEK_CUR时,文件偏移量为当前位置加offset,offset可正可负。 
返回值 
成功返回新的文件偏移量,失败则返回-1。

函数read:从打开的文件中读数据

函数原型

  1. #include<unistd.h>
  2. ssize_t read(int fd,void* buf,size_t count);

功能说明:从文件描述符为fd的文件中读出count字节的数据并存放在buf中。若read成功则返回读到的字节数,若已经到达文件末尾则返回0,若出错返回-1. 
有以下几种情况读到的实际字节数小于count: 
在未读够count字节但已经到达文件末尾时返回实际读到的字节数。下一次读时返回0. 
从终端设备读时,通常一次最多读一行。 
从网络读时,由于网络缓冲机制可能会造成返回值小于count。 
从管道或FIFO读时,若管道包含字节数小于count,则返回实际字节数。 
从一些面向记录的设备中读时,一次最多返回一个记录。

write函数

函数原型

  1. #include<unistd.h>
  2. ssize_t write(int fd ,void* buf,size_t count);

功能说明:从buf中向文件描述符为fd的文件写count字节的数据,若成功则返回写入的字节数,若出错返回-1,返回值通常和count相等,否则表示出错。

原子操作

原子操作指有多步组成的操作,原子操作在执行时,要么执行完所有的步骤,要么就一步都不执行。为什么要引入原子操作这个概念呢? 
下面写了一个简单的测试程序,观察一下它的执行结果,体会一下原子操作的必要性。

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<stdlib.h>
  4. #include<fcntl.h>
  5. int main(void)
  6. {
  7. char buf[3]={'a','b','c'};
  8. char tmp[3]={'q','w','e'};
  9. int num;
  10. int fd;
  11. int fd1;
  12. pid_t pid;
  13. pid=fork();
  14. if(pid==0)
  15. {
  16. if((fd=open("yy",O_RDWR))<0)
  17. {
  18. printf("open error");
  19. exit(-1);
  20. }
  21. if(lseek(fd,0,SEEK_END)<0)
  22. {
  23. printf("lseek error");
  24. exit(-1);
  25. }
  26. sleep(2);
  27. if(write(fd,tmp,3)!=3)
  28. {
  29. printf("write error");
  30. exit(-1);
  31. }
  32. }
  33. else{
  34. sleep(1);
  35. if((fd1=open("yy",O_RDWR))<0)
  36. {
  37. printf("open error");
  38. exit(-1);
  39. }
  40. if(lseek(fd1,0,SEEK_END)<0)
  41. {
  42. printf("lseek error");
  43. exit(-1);
  44. }
  45. if(write(fd1,buf,3)!=3)
  46. {
  47. printf("write error");
  48. exit(-1);
  49. }
  50. }
  51. }

在这个程序中有两个进程对同一个文件进行写操作,由于是先定位,再写这种模式,子进程先定位但还未写文件偏移量为0,子进程被挂起,执行父进程,父进程定位紧接着写入abc,这时文件尾发生了改变。然后子进程继续执行但它不知道文件尾发生了改变,子进程以为自己还是写在文件尾,这样会导致子进程写的qwe会覆盖父进程写的abc。最终yy文件中只保存qwe。

函数dup和dup2

函数原型

  1. #include<unistd.h>
  2. int dup(in fd);
  3. int dup2(int fd, int fd2);

功能说明:这两个函数都是复制一个现有的文件描述符。dup函数返回的新描述符是当前可用描述符中最小的一个。而dup2函数可以通过参数fd2指定新的描述符,如果fd2已经打开就关闭它,如果fd等于fd2则返回返回fd2,不关闭。否则fd2的FD_CLOEXEC文件描述符标志就被清除。这样fd2在进程调用exec时是打开状态。

函数 fcntl

函数原型

  1. #include<fcntl.h>
  2. int fcntl(int fd,int cmd,...../*int arg*/);

功能说明: 
复制一个已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)。 
获取/设置文件描述符(cmd=F_GETFD或F_SETFD)。 
获取/设置文件状态标志(cmd=F_GETFL或F_SETFL)。 
获取/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)。 
获取/设置记录锁(cmd=F_GETLK或F_SETLKW)。 
详细用法参见man手册。 
这里用fcntl函数模拟一下dup和dup2

  1. dup(fd)

等效于

  1. fcntl(fd,F_DUPFD,0);
  1. dup2(fd,fd2);

等效于

  1. close(fd2);
  2. fcntl(fd,F_DUPFD,fd);

下面是通过指定cmd参数获取文件状态的程序

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. #include<fcntl.h>
  4. #include<stdlib.h>
  5. int main(int argc,char** argv)
  6. {
  7. int fd;
  8. int var;
  9. if(argc!=2)
  10. {
  11. printf("usage: a.out <descriptor#>");
  12. exit(-1);
  13. }
  14. int file=atoi(argv[1]);
  15. if((var=fcntl(file,F_GETFL,0))<0)
  16. {
  17. printf("fcntl error for fd:%d\n",file);
  18. exit(-1);
  19. }
  20. switch(var & O_ACCMODE)
  21. {
  22. case O_RDONLY:
  23. printf("read only");
  24. break;
  25. case O_WRONLY:
  26. printf("write only");
  27. break;
  28. case O_RDWR:
  29. printf("read write");
  30. break;
  31. default:
  32. printf("unknown access mode");
  33. }
  34. if(var & O_APPEND)
  35. printf(", append");
  36. if(var & O_NONBLOCK)
  37. printf(", noblocking");
  38. if(var & O_SYNC)
  39. printf(", synchronous writes");
  40. putchar('\n');
  41. exit(0);
  42. }

文件I/O和标准I/O的区别

一、先来了解下什么是文件I/O和标准I/O: 文件I/O:文件I/O称之为不带缓存的IO(unbuffered I/O)。不带缓存指的是每个read,write都调用内核中的一个系统调用。也就是一般所...
  • zqixiao_09
  • zqixiao_09
  • 2016年01月01日 10:12
  • 2529

python中的文件I/O

读/写文件: 常规操作 读文件:f = open('文件路径',‘r‘ - 读文本文件 ‘rb' - 读二进制文件 encoding = 'utf-8' - 指定读取时的字符编码) ...
  • Ayhan_huang
  • Ayhan_huang
  • 2017年06月13日 20:06
  • 260

Linux学习总结(四)——标准I/O与文件I/O

在应用开发中,经常要访问文件,Linux 下的文件读写方式分为两大类:标准 I/O 和 文件 I/O,下面分别介绍下两种 I/O 的相关操作,并比较下两种 I/O 的特点。 一、标准I/O标准I/O...
  • myintelex
  • myintelex
  • 2016年12月26日 18:29
  • 182

IOS的文件I/O

#import "ViewController.h" @interface ViewController () { NSString *myPath; NSString *opera...
  • IOS_dashen
  • IOS_dashen
  • 2015年09月02日 15:21
  • 370

嵌入式LINUX下I/O资源的实现、管理和操作

作者:wincemobile 转自:http://blog.csdn.net/wincemobile/article/details/5898990 几乎每一种外设都是通过读写设备...
  • whw8007
  • whw8007
  • 2014年02月12日 08:57
  • 637

用C++实现文件I/O操作

文件 I/O 在C++中比烤蛋糕简单多了。 在这篇文章里,我会详细解释ASCII和二进制文件的输入输出的每个细节,值得注意的是,所有这些都是用C++完成的。  一、ASCII 输出  为了使用下面的方...
  • jiahehao
  • jiahehao
  • 2007年11月01日 15:51
  • 1647

网络和I/O

一、网络HttpURLConnection 类HttpURLConnection 类是基于HTTP协议的,其底层是通过Socket实现的。URL请求的类别: 分为二类,GET与POST请求。二者的...
  • qq_30154277
  • qq_30154277
  • 2016年07月21日 12:51
  • 610

使用I/O流实现文件的复制

使用字节输入、输出流实现文件的复制 使用I/O流实现文件复制其实就是将文件内内容读出来,再将读出的内容写入指定的文件的过程,以下为读出部分的代码: //实例化一个文件(用于读取内容) File fil...
  • qq_38446250
  • qq_38446250
  • 2017年05月27日 11:27
  • 131

MATLAB文件I/O指南(2)MATLAB中的文件I/O介绍

1、MATLAB中的文件输入/输出介绍支持的文件格式 MATLAB可读入的文件格式以及用来读取的函数:(所有文件I/O程序不需要特殊的工具箱)(可在MATLAB help 里搜索Supported ...
  • HateCode
  • HateCode
  • 2006年12月15日 02:54
  • 2607

Matlab的I/O文件操作使用技巧和总结:

Matlab的I/O文件操作使用技巧和总结:
  • cherry4500
  • cherry4500
  • 2016年01月25日 14:35
  • 1998
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:文件I/O
举报原因:
原因补充:

(最多只允许输入30个字)