I/O 操作

I/O操作

一.分类

1.      按是否带有缓存分类,可分为:带缓存的(行缓存、全缓存)和  不带缓存的

2.      按函数类型分类,可分为:系统调用函数(open、close …)  和  标准C库函数 (fopen 、 fclose …)

二.函数概览:

1.      标准C库函数:

1)      打开和关闭

fopen  freopen(流重定向)  fclose

2)      文件读和写

fread  fwrite  fscanf fprintf  fgetc  fputc fgets  fputs      

3)      其它

setbuf  setvbuf (设置缓冲类型)  fflush(强制刷新缓存)  feof(判断是否到文件末尾)  fseek(文件读写位置移动)  ftell(返回当前文件读写位置)  rewind(移动文件读写位置到文件头)

2.      系统调用函数:

1)      打开创建 和 关闭

open create  close    

2)      读写和读写位置移动

read  write  lseek

3.      文件夹及文件属性操作

stat   fstat

产看文件属性

chmod  fchmod

更改文件访问权限

chown  fchown  lchown

更改文件所有者

readdir  mkdir  rmdir  chdir  opendir

读、建、删、更改、打开  目录

link  symlink  readsymlink

 

ulink  remove  rename

删除、删除、重命名文件

access

 

truncate  ftruncate

清空文件

umask

设置当前进程 创建文件掩码

 

三.名词解释:

1.      流:所有的I/O操作仅是简单的从程序移近或者移除,这种字节流就成为流。

2.       文件指针: 更准确的说,应该叫做文件结构体指针。每个被使用的文件都在内存中开辟一个区域,用来存放文件的有关信息,这些信息保存在一个结构体类型的变量中,该结构体类型由系统定义,取名为FILE

3.      文件描述符:顺序分配的非负整数,内核用以标志一个特定进程打开的文件。一定是最小未使用的一个非负整数。 close(0);  open(“1.txt”, ”r”) 返回的应该是0

当使用系统调用读写文件时,用open或create返回的文件描述符。

4.      文本流:在流中处理的数据是以字符出现。 在文本流中,’\n’被转化成回车符CR 和换行符LFASCODH OAH 而当输出时,反之。

如 ’2’  ’0’  ’1’  ’1’              -->>    ASC: 50   48  48  49

5.      二进制流:在流中处理的是二进制序列。  若流中有字符,则用一个字节的二进制ASC码表示;若是数字,则用对应的二进制表示,对 ’\n’不做变换。

如 数字2001                            -->>     0000  0111   1101  0001

6.      全缓存:当填满I/O缓存后才进行实际的I/O操作,或者满足一定条件后,系统通过malloc来获得所需要的缓冲区。当缓冲区满(长度通常为8192B),或者满足一定条件,会执行刷新操作。(也可以手动调用fflush(FILE *fp)来手动强制刷新)

可做实验 :   将一下代码补全后,运行等待几分钟使用关闭C-c  看文件是否被写入

While(1)

{

           fprintf(fp,“aaaaaa”);

           sleep(1);

}

7.       行缓存:当在输入或者输出中遇到新行符(’\n’)时,进行I/O操作。

可做实验:  补全以下代码,观察结果是否和想象的一样。

printf(“aaaaaaaaaaaaaaaaaaaa”);

while(1);

8.       不带缓存: 标准I/O库不对字符设备进行缓存, 例如stderr 很多人机交互界面要求不可全缓存,标准出错绝不会是全缓存使用setbuf() setvbuf()可以更改缓存类型。

四.Tip

1.      文件打开

当一个程序被执行,系统将自动为其打开三个标准I/O“文件”。

类别

文件描述符

文件结构体指针

标准输入

STDIN_FILENO  (0)

Stdin

标准输出

STDOUT_FILENO  (1)

Stdout

标准错误输出(不带缓存)

STDERR_FILENO  (2)

Stderr

1)      fopen  FILE *fopen(const char *path, const char*mode)

模式标志位:

r

以只读方式打开,文件必须存在,否则打开失败,返回NULL

r+

以读写方式打开,文件必须存在,否则同r

w

以只写方式打开,若不存在则创建,若存在则清空原文件

w+

以可读可写方式打开,同w

a

以附加方式打开,若不存在则创建文件,若存在,写入数据会始终被添加到文件尾,不会修改到原文件内容

a+

以可读写附加方式打开,若不存在则创建,读文件默认在文件头,写文件始终在文件末尾,不会修改到原文件内容

当给定b”参数时,表示以二进制方式打开文件。

当文件不存在创建时, fopen不能设定文件权限参数(open可设定)。系统会默认采用(0666 &  ~umask)作为新文件的权限

2)      open  int open(const char *pathname,  intflag,  mode_t  mode)

flag

O_RDONLY

只读方式打开

这三个参数互斥!

且这三个参数必须有一个!

O_WRONLY

只写方式打开

O_RDWR

读写方式打开

O_CREAT

如果文件不存在则创建新文件。 并用第三个参数为其设置权限。

O_EXCL

当使用O_CREAT选项时文件存在,则返回出错

O_NOCTTY

 

O_TRUNC

如文件存在,那么打开文件先删除文件中原有数据

O_APPEND

以附加方式打开文件,所有对文件的写操作都在文件的末尾进行

mode  为权限标志, 可以使用8进制表示 如 rw-rw-r--   à   0664, 也可以使用位或得到,如:0664   à  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |S_OROTH

open可以打开设备文件,但是不能创建设备文件,创建设备文件,必须使用mknod

2.      文件关闭 

1)      fclose          int fclose(FILE*stream);

当关闭一个文件时,系统会自动刷新缓冲区中的数据。当系统正常结束时(直接调用exit函数或从main函数返回),则所有带有写缓存的数据的标准I/O流都会被刷新,所有打开的标准I/O流都会被关闭

在调用fclose关闭流后对流所进行的任何操作,包括fclose其结果都是未知的。

2)      close         intclose (int fildes)

进程终止时,该进程打开的所有文件都由内核关闭,关闭一个文件同时,也是放该进程加在该文件上的所有记录锁。

3.      文件读

fgetc(getchar)  fgets(gets)   fscanf(scanf)   fread

gets函数是不被推荐的,因为函数的使用者不能指定缓存的长度,这样就可能造成缓存越界

fgets指定缓存长度为n此函数将读到至多n-1个字符,会在末尾填 ’\0’ 在遇到’\n’或则文件结束时结束

fread函数返回的不是读取的字节数,而是读取的对象数(向下取整)。

 

4.      文件写

fputc   fgets  fprintf   fwrite

fputs函数将一个以’\0’结束的字符串输出到指定的流。终止符’\0’不会被输出。并不一定只输出一行(并没有要求只能是’\0’前是’\n’)。

puts 函数将一个以’\0’结束的字符串输出到标准输出流上。终止符’\0’不会被输出. 但是puts 最后会补上一个换行符。

fwrite 函数返回的不是写入的字节数,而是写入的对象数(向下取整)。

5.      文件读写位置控制

ftell  fseek  都假定文件的位置可以放在一个长整型中。fgetpos   fsetpos是ANSI C引入的,同时引入新类型fpos_t。

ftell  返回当前读写指针位置,失败返回-1L。

fseek 移动读写指针,较第三个参数一定偏移量。 SEEK_SET 文件头, SEEK_CUR 当前位置, SEEK_END 文件结束

lseek 类似于fseek。  Lseek 只对常规文件有效socketpipefifo等都失败。

                  本操作不会引起任何I/O操作。  文件偏移可以大于文件的长度,并形成空洞。

rewind 移动文件读写指针到文件开始。相当 fseek(fp, 0L, SEEK_SET)

6.      临时文件

tmpnam   char*tmpnam(char *s)

           产生一个与现在文件名不同的一个有效路径名字符串,每次调用都会产生不同的一个路径名。 sNULL,则返回值放在一个静态区中,否则则认为s指向的是一个长度至少为L_tmpname个字节空间

Tmpfile  FILE*tmpfile(void)

           创建一个临时二进制文件(以wb+方式打开),在关闭文件或程序结束时,会自动删除这种文件。

7.      文件出错函数

feof   ferror   clearer

8.      文件属性操作

1)      stat  fstat lstat

stat            intstat(const char *file_name, struct stat  *buf);

函数执行状态通过返回值返回,  执行结果通过  buf返回(空间有调用者开辟)。

struct stat {

                  dev_t         st_dev;      /* device */

                  ino_t         st_ino;      /* inode */

                  mode_t       st_mode; /* protection*/文件类型和读写执行权限

                  nlink_t       st_nlink;    /* number of hard links */

                  uid_t         st_uid;      /* user ID of owner */

                  gid_t         st_gid;      /* group ID of owner */

                  dev_t         st_rdev;     /* device type (if inode device) */

                  off_t         st_size;     /* total size, in bytes */

                  blksize_t     st_blksize;  /* blocksize for filesystem I/O */

                  blkcnt_t      st_blocks;   /* number of blocks allocated */

                  time_t        st_atime;    /* time of last access */

                  time_t        st_mtime;    /* time of last modification */

                  time_t        st_ctime;    /* time of last change */

      };

2)      unask

umask         mode_t  umask(mode_t mask);

类似unix系统提供的umask命令的作用。  不同点是,函数umask 只修改当前进程创建文件的掩码

3)      truncate  ftruncate

int  truncate(const  char *path,  off_t length);

length可以大于文件的实际长度,  若小于,则超过length外的长度不再能访问,若大于,则扩展文件,文件产生空洞

9.      全缓冲、行缓冲、不缓冲

1)       全缓冲,只有 fflush()缓冲区满、程序正常结束(return 或者调用exit注:调用_exit _Exit不会刷新缓存)才会进行I/O操作。通常大小为8192(8KB)或者4096(4KB).

2)       行缓存,只用遇到新行符’\n’ 调用fflush 、缓冲区满、程序正常结束,才会进行I/O操作。大小通常为 1024(1KB)

3)      不缓存,所有标准I/O函数都会进行I/O操作。

4)       标准错误输出 stderr   always unbuffered  一直无缓存

5)      标准输入和标准输出是全缓存。  除非关联到一个终端设备(terminal device),此时是行缓存。

6)       除以上三个以外所有流,  除非关联到一个终端设备,否则都是全缓存。

7)      可以使用  setbuf    setvbuf 更改缓冲类型。   可以使用fflush   强制刷新流。

8)      exit   _exit   _Exit   

exit,调用之后,会执行一系列的清理操作,包括调用执行各终止处理程序,关闭所有标准I/O流,然后进入内核。

_exit  _Exit 类似,不会进行清理工作而直接进入内核。

五.应用实践

1.      实现模拟写日志

1)      每行数据类似于, 1,2013-8-13 22:30:33

2)      改程序无限循环,直到C-C中断

3)      再次启动程序时,若原日志文件存在,则接上序号。

2.      简单的ls  -l  的实现。 打印出类似的信息

3.      简单的cp 的实现。

4.      简单的grep的实现。

六.Q&A

1.      Q: 如何输入EOF, 如 while(scanf(“%d”,&num));  输入什么时,程序才会结束?

A: C-e   ctrl + e

2.      Q: 如何在文件中插入或者删除一行?

A:通过临时文件

3.      Q: 如何把stdin或者stdout 重定向到文件?

A:使用freopen

4.      Q: 如何恢复3中的重定向?

A:   再次使用freopen, 如 freopen(“/dev/tty”,“w”, stdout)

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值