Linux操作系统—标准I/O库(1)(2015-8-3)

分类:Linux操作系统

  不仅在linux,在很多操作系统上都实现了标准I/O库,该库由ANSI C标准说明。标准I/O库是在系统调用函数基础上构造的,它处理很多细节(例如缓存分配)以优化执行I/O。与基于文件描述符的I/O相比,基于流的I/O更加简单,方便,也更加高效。因而在Linux环境C程序的编写中,基于流的I/O使用更为广泛。

流和文件指针

  在基于文件描述符中的底层系统调用的I/O操作中,所有的I/O函数都是针对文件描述符的。当打开一个文件时,即返回一个文件描述符,然后该问价描述符就用于后续的I/O操作。
  在标准I/O库中,所有的I/O操作都是围绕流(stream)来进行的。在Linux中,文件和设备都可以看做是数据流。
  什么是数据流?数据流是指无结构的字节序列。当用标准I/O库打开或创建一个文件时,即将一个流与一个文件结合起来。
  标准I/O库提供了函数fopen用于打开一个流。当打开一个流时,该函数会返回一个指向FILE对象的指针,即文件指针(类型为FILE *)。FILE对象通常是一个结构,它包含了I/O库为管理该流所需要的所有信息:用于实际I/O的文件描述符,指向流缓存的指针,缓存长度,当前在缓存中的字节数,出错标志等。但一般应用程序没有必要关心FILE结构体的各成员值,使用流时,只需要将FILE指针作为参数传递给每个标准I/O函数。
  Linux对一个进程预定义了三个流:标准输入流,标准输出流和标准错误输出流,它们自动地为进程打开并可用。这三个标准I/O流通过在头文件<stdio.h>中预定义的文件指针stdin,stdout和stderr加以引用。它们与文件描述符STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO所表示的标准输入,标准输出和标准错误输出相对应。

缓存

  为什么要有缓存?
  标准I/O库(注意:标准I/O库是在系统调用函数基础上构造的)提供缓存的目的是尽可能减少使用read和write调用的次数,以提高I/O效率。标准I/O也对每个I/O流自动进行缓存管理,免除了由应用程序考虑这一点的麻烦。
  标准I/O提供了三种类型的缓存。
- 全缓存

  使用全缓存时,只有当标准I/O缓存填满后才进行实际的I/O操作。
  对磁盘文件的标准I/O操作一般是实施全缓存的。在一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc函数分配所需要使用的缓存。
  术语刷新(flush)用于说明标准I/O缓存的写操作。缓存可由标准I/O例程自动刷新(比如当填满一个缓存时),或者可以调用函数fflush显示刷新一个流。

  • 行缓存

  使用行缓存时,标准I/O库会在输入和输出中遇到换行符时执行实际的I/O操作。当流涉及一个终端时(例如标准输入和标准输出),典型地使用行缓存。
  对行缓存有两个限制:
1. 因为行的缓存长度是固定的,所以只要填满了缓存,即使还没有写一个换行符,也会进行I/O操作。
2. 任何时候只要通过标准I/O库要求从一个不带缓存的流或一个行缓存的流(它预先要求从内核中得到数据,所需要的数据可能已在该缓存中)得到输入的数据,那么就会造成刷新所有的行缓存输出流。

  • 不带缓存

  即不对字符进行缓存。如果用标准I/O函数写若干条字符到不带缓存的流中,则相当于用write系统调用函数将这些字符写至相关联的打开文件上。标准错误输出流stderr通常是不带缓存的,这就使得出错信息可以尽快显示出来,而不管它们是否含有一个新行字符。

ANSI C规定了下列缓存特征:
- 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
- 标准错误输出绝对不会是全缓存的。

流的打开和关闭

  标准I/O库提供了fopen系列函数用以创建或打开流文件,提供了fclose函数关闭已打开的流文件。

打开流

  使用fopen系列函数可以创建或打开流文件,这些函数的原型如下。

#include <stdio.h>FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);

  这三个函数的区别如下:
(1)fopen打开路径名由path指定的一个文件。
(2)freopen在由stream指定的流上打开一个指定的文件(其路径由path指定),如若该流已经打开,则先关闭该流。次函数一般用于将一个指定的文件打开为一个预定义的标准流:标准输入,标准输出或者标准错误输出。
(3)fdopen取一个现存的文件描述符,并使一个标准的I/O流与该描述相结合。此函数常用于创建管道和网络通信通道函数获得的描述符。因为这些特殊类型的文件不能用标准I/O函数fopen打开,而必须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一个标准I/O流与该描述符相结合。注意:fdopen函数不是ANSI C的标准函数,而是属于POSIX.1的标准。
  这三个函数各参数和返回值的含义如下:
1. path:要打开或创建的文件的名字
2. mode:对该I/O流的读,写方式,ANSI C规定了15种不同的可能值

  • r或rb:以读方式打开
  • w或wb:以写方式打开或创建,并将文件长度截为0
  • a或ab:以写方式打开,新内容追加在文件尾
  • r+或r+b或rb+:以更新方式打开(读和写)
  • w+或w+b或wb+:以更新方式打开,并将文件长度截为0
  • a+或a+b或ab+:以更新方式打开,新内容追加在文件尾

  注意:(1)字符b的作用是区分文本文件和二进制文件。但Linux内核并不对这两种文件进行区分,所以在Linux系统环境下字符b作为mode的一部分实际上并无作用。(2)对于fdopen,因为该描述符已经被打开,即所引用的文件必已存在,所以fdopen以写方式打开并不会创建该文件或截短该文件。
3-fd:待关联的底层文件描述符
4-stream:待关联的流的文件指针,若该流已经打开则会被先关闭
5-返回值:成功时返回流文件的指针,失败时返回NULL
  能否成功打开文件流受打开方式的限制,如下表所示。另外,进程可打开的文件流的数量有一个上限,该上限值由stdio.h中定义的FOPEN_MAX常量定义。
  打开一个I/O流的六种不同方式的限制

限制 r w a r+ w+ a+
文件必须已存在
擦除文件以前的内容
流可读
流可写
流只可在尾端写

  在指定w或a类型创建一个新文件时,无法指定该文件的存取许可权位,POSIX.1要求这种方式创建的文件具有以下存取许可权。
S_USR | S_WUSR | S_RGRP | S_WGRP | S_ROTH | S_WOTH
  除非流引用终端设备,否则系统默认它被打开时是全缓存的。若流引用终端设备,则该流四行缓存的。

关闭流

  在使用完流文件后,应调用fclose函数关闭流。fclose函数原型如下:

#include <stdio.h>
int fclose(FILE *fp);

  函数成功时返回0,失败时返回EOF(-1)
  在文件流被关闭之前,fclose会刷新缓存中的输出数据,但缓存中的输入数据将被丢弃。如果标准I/O库已经为该流自动分配了一个缓存,则释放此缓存。
  当一个进程正常终止时(直接调用exit函数,或从main函数返回),则所有带未写缓存数据的标准I/O流都被刷新,所有打开的标准I/O流都被关闭。

实践篇

   目标一:在当前目录下创建一个file.txt文件
方法一:

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{FILE *fp = NULL;fp = fopen("file.txt", "w");if (fp == NULL){printf("Cannot create files!\n");exit(-1);}fclose(fp);return 0;
}

  该程序能够达到预定目标,它在我的机器上的运行结果如下:

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gedit fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o fopen fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./fopen
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
file.txt  fopen  fopen.c  fopen.c~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ll file.txt
-rw-rw-r-- 1 biantiao biantiao 0  8月  4 08:57 file.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

运行结束后,它在当前目录下创建了一个空的file.txt文件。

方法二:

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{FILE *fp = NULL;fp = fopen("file.txt", "r");if (fp == NULL){printf("Cannot create files!\n");exit(-1);}fclose(fp);return 0;
}

  该方法和上面的区别是,fopen中的参数由w换成了r,结果怎样呢?结果是不行的,它在我的机器上的运行结果如下:

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gedit fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o fopen fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
fopen  fopen.c  fopen.c~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./fopen
Cannot create files!
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ll
总用量 28
drwxr-xr-x  2 biantiao biantiao 4096  8月  4 09:04 ./
drwxr-xr-x 46 biantiao biantiao 4096  8月  3 21:41 ../
-rwxrwxr-x  1 biantiao biantiao 8704  8月  4 09:04 fopen*
-rw-rw-r--  1 biantiao biantiao  220  8月  4 09:01 fopen.c
-rw-rw-r--  1 biantiao biantiao  220  8月  4 08:56 fopen.c~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

  应该能够明白创建文件该用哪个参数了吧。注意w和r的区别,如下:

r或rb:以读的方式打开(注意只能打开,不能创建)
w或wb:以写方式打开或创建,并将文件长度截为0

  写到这里我又想写一个小程序来验证上面的话,真心不好意思,初学者好奇心很重。说干就干。验证什么呢?验证以w方式打开一个文件能将其长度截为0。首先先在当前目录创建一个test.txt文件,在里面输入一句hello world。如下图:

  编写下列程序,保存为cut.c

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{FILE *fp = NULL;fp = fopen("test.txt", "w");if (fp == NULL){printf("Open file failed!\n");exit(-1);}fclose(fp);return 0;
}

  从上面的程序可以看出,代码只是将已存在的test.txt文件打开,然后关闭。看看test.txt文件会有什么变化。原先test.txt文件中写有一句Hello World!。编译并运行,查看结果。

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o cut cut.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
cut  cut.c  test.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./cut
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ cat test.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

  从命令行中cat的结果来看,test.txt文件确实被截短为0了。为了更直观我用gedit打开看看。如下图:

文件确实被截短为0了!(下次编程时要注意了,如果不想文件原来的内容被截掉,不能使用w参数,而得使用a或r参数来实现)

Linux操作系统-标准IO库(1)相关推荐

  1. Linux操作系统-标准IO库(2)

    Linux操作系统-标准IO库(2)(2015-8-4) 分类:Linux操作系统   打开一个流后,可采用三种不同类型的非格式化I/O对其进行读,写操作. 1. 每次读取一个字符的I/O 2. 每次 ...

  2. Linux操作系统-标准IO库(3)

    Linux操作系统-标准IO库(3)(2015-8-5) 分类:Linux操作系统 二进制IO和定位流   二进制I/O也称直接I/O,一个一个对象的I/O,面向记录的I/O或面向结构的I/O.每次I ...

  3. 【Linux】标准IO库

    Linux系统的文件IO都是针对文件描述符的,而标准IO(ISO C)的操作则是围绕流进行的,一个最明显的区别是标准IO比Linux文件IO多了缓冲机制.为了使用流,需要用到文件指针即指向FILE结构 ...

  4. linux 流函数,标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流

    1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. ...

  5. Linux操作与管理文件(多次打开同一文件,文件共享,fcntl函数,标准IO库)

    1.linux系统如何管理文件 硬盘中的静态文件和inode: (1)硬盘分为两大区域:一个是硬盘内容管理表项,另一个是真正存储内容的区域.先去读取硬盘内容管理表,找到要访问的存储内容的区域,再用得到 ...

  6. Linux后台开发系列之「13.标准 IO 库」

    StdIO.png 版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处! 标准 IO 库 上一篇文章我们学习了 5 个底层的 IO 函数,这次我们来学习标准的 I ...

  7. 标准IO库--unix环境高级编程读书笔记

    标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...

  8. 在标准IO库中,rewind函数作用?

    在标准IO库中,rewind函数作用? 将文件内部的位置指针重新指向一个流(数据流/文件)的开头 一个完整的信号生命周期包含4个重要的事件,这4个重要事件分别是? 信号诞生:信号在进程中注册完毕:信号 ...

  9. C++ Primer 第八章 标准IO库

    学习本章内容之前有必要对缓冲区的概念做一个基本了解,我引用了网上一片文章<C++编程对缓冲区的理解>,内容如下: 什么是缓冲区    缓冲区又称为缓存,它是内存空间的一部分.也就是说,在内 ...

最新文章

  1. CStopWatch计时器的用法实例
  2. 允许使用抽象类类型 isearchboxinfo 的对象_Java面向对象编程三大特征 - 多态
  3. 花五分钟看这篇之前,你才发现你不懂RESTful
  4. 服务器php 启动命令_服务端的cli方式运行
  5. Git学习笔记(四)
  6. 【五级流水线CPU】—— 4. 移动操作指令(6条)
  7. php laravel手册,laravel5.6手册下载|Laravel5.6中文手册pdf最新版下载(附使用方法)_星星软件园...
  8. tabbar角标 小程序_关于小程序tabbar不支持传参的处理办法
  9. ABAP 关于 delete adjacent duplicates from的小心得
  10. 分享109个PHP源码,总有一款适合您
  11. ubantu 安装 mosquitto时 connection refused 的解决办法
  12. 美国访学的一些心得体会与注意事项
  13. 高德地图广告投放的优势、效果!
  14. 实验1 蓝桥ROS1机器人入门 适用kinetic/melodic/noetic
  15. Debug下正常运行,但调成Release时遇到三个问题及其完美解决!
  16. 怎样用excel按进行分类求和,最后再根据一列对其他列进行排序
  17. 解析光纤跳线的5大知识点,让安装使用更顺畅
  18. 光伏行业十个人的江湖:霸道总裁pk硬汉书生
  19. php调试加密代码,使用bcompiler对PHP文件进行加密的代码
  20. 马尔奖得主 Alan Yuille | AI 视觉的未来:像人一样看世界

热门文章

  1. 【Java版oj】day31美国节日、分解因数
  2. 基因ID命名及相互转换
  3. ImageMagick简介及试用
  4. 微信小程序 引入日历组件
  5. Flowable实战(一):启动事件与结束事件
  6. 小米PK华为,智能家居这场战争谁能笑到最后
  7. Hive数据倾斜解决要点
  8. TP3.2+图片上传腾讯云存储(详细教程)
  9. 编程精华资源大汇总 (转)
  10. 在这一周一度的周五,聊聊我陀螺般的日常