标准 I/O 库(stdio)及其头文件 stdio.h 为底层 I/O 系统调用提供了一个通用的接口。这个库现在已经成为 ANSI 标准 C 的一部分。标准 I/O 库提供了许多复杂的函数用于格式化输出和扫描输入。在很多方面,你使用的标准 I/O 库的方式和使用底层文件描述符一样,需要先打开一个文件以建立一个访问路径,这个操作的返回值将作为其他 I/O 库函数的参数。在标准 I/O 库中,与底层文件描述符对应的是(stream,需要注意的是这个流与 C++ 中的输入输出流不一样),它被实现为指向结构 FILE 的指针文件指针)。

  在启动程序时,有 3 个文件流是自动打开的,它们是 stdin、stdout 和 stderr,在 stdio.h 中定义,分别代表着标准输入、标准输出和标准错误输出,与底层文件描述符 0、1、2 相对应。可用的文件流数量与文件描述符一样,都是有限制的,实际的限制由头文件 stdio.h 中定义的 FOPEN_MAX 来定义,它的值至少为 8,在 Linux 系统中,通常是 16。

一、fopen 函数

  用于文件和终端的输入输出,,类似于系统调用 open。调用 fopen 函数成功时,返回一个非空的 FILE * 指针,调用失败时,返回 NULL。函数原型如下:

#include <stdio.h>

FILE *fopen(const char *filename,const char *mode);

  关于 FILE 结构,有:

typedef struct _iobuf{
int cnt; // 剩余字符数
char *ptr; // 下一个字符的位置
char *base; // 缓冲区的位置
int flag; // 文件访问模式
int fd; // 文件描述符
}FILE;

  fopen 打开由 filename 参数指定的文件,并把它与一个文件流(即 fopen 函数的返回值,一个指向 FILE 的指针)关联起来。mode 参数指定文件打开的方式,可以取以下的一些值:

字符串 说明
"r" 、"rb" 以只读方式打开文件
"w" 、"wb" 以写方式打开文件,并把文件长度截短为零
"a" 、"ab" 以写方式打开文件,新内容追加在文件尾
"r+" 、"rb+" 、"r+b" 以更新方式打开文件(读和写)
"w+" 、"wb+" 、"w+b" 以更新方式打开文件,并把文件长度截短为零
"a+" 、"ab+" 、"a+b" 以更新方式打开文件,新内容追加在文件尾

  其中,字母 b 表示文件是一个二进制文件而不是文本文件。另外,需要注意的是,mode 参数是一个字符串而不是一个字符,因此总是需要使用双引号 " " 括起来。

二、fread 函数

  fread 函数用于从一个文件流里读取数据。调用 fread 成功时,返回成功读到缓冲区里的记录个数(不是字节数)。函数原型如下:

#include <stdio.h>

size_t fread(void *ptr,size_t size,size_t nitems,FILE *stream);

  数据从文件流 stream 读到由 ptr 指向的数据缓冲区里,size 参数指定每个数据记录的长度,计数器 nitems 给出要传输的记录个数。举个例子:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> int main()
{
int buffer[];
int i;
int counter;
counter = fread(buffer,,,stdin);
printf("读取到缓冲区的记录个数为:%d\n",counter);
fwrite(buffer,sizeof(int),,stdout); exit();
}

  这段代码从标准输入读取 10 个 字符到缓冲区中,然后调用 fwrite 函数将缓冲区中的前 4 个字符写到标准输出。

三、fwrite 函数

  fwrite 函数从指定的数据缓冲区里取出数据记录,并把它们写到输出流中,返回成功写入的记录个数,其参数与 fread 函数的参数类似。

#include <stdio.h>

siize_t fwrite(const void *ptr,size_t size,size_t nitems,FILE *stream);

  需要注意的是,用 fwrite 写的文件在不同的计算机体系结构之间可能不具备可移植性,因此,不推荐把 fread 和 fwrite 用于结构化数据。演示代码如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> int main()
{
char buffer[] = "hello,my name is tongye!";
fwrite(buffer,,sizeof(buffer),stdout);
exit();
} /* 输出结果:
hello,my name is tongye!
*/

  这段代码将 buffer 中的内容写入标准输出。

四、fclose 函数

  fclose 函数关闭指定的文件流 stream,使所有尚未写出的数据都写出。因为 stdio 库会对数据进行缓冲,所以使用 fclose 是很重要的。如果程序需要确保数据已全部写出,则应该调用 fclose 函数。

#include <stdio.h>

int fclose(FILE *stream);

五、fflush 函数

  fflush 函数的作用是把文件流里所有未写出的数据立刻写出。

#include <stdio.h>

int fflush(FILE *stream);

  在 调用 fclose 函数时,隐含执行了一次 flush 操作,因此不需要再调用 fclose 之前调用 fflush。

六、fseek 函数

  fseek 函数是与 lseek 系统调用对应的文件流函数。它在文件流里为下一次读写操作指定位置。当调用 fseek 成功时,返回 0,调用失败时 返回 -1 并设置 errno 指出错误。

#include <stdio.h>

int fseek(FILE *stream,long int offset,int whence);

七、fgetc、getc 和 getchar 函数

  fgetc 函数从文件流里取出下一个字节,并把它作为一个字符返回(返回的是该字符的 ASCII 码)。当它到达文件尾或出现错误时,返回 EOF(在 stdio.h 中有定义: #define EOF -1)。必须通过 ferror 或 feof 来区分这两种情况。函数原型如下:

#include <stdio.h>

int fgetc(FILE *stream);    // 注意,返回值是一个字符,但是这里用 int 类型来存放字符而不是用 char 类型
int getc(FILE *stream);
int getchar();

  getc 函数的作用和 fgetc 函数一样,但是,getc 函数可以被实现为宏,因此:

1)getc 函数的参数不应该是具有副作用(如影响变量)的表达式,因为它可能会被计算多次;

2)fgetc 一定是一个函数,因此它的地址可以作为一个参数传递给另一个函数;而 gets 函数则不能保证其地址一定能作为一个函数指针传递给另一个函数;

3)由于 getc 函数可以被实现为宏,因此调用 getc 函数所用的时间可能会比 fgetc 要短。

  getchar 函数的作用就相当于 getc(stdin),它直接从标准输入里读取下一个字符。

注意:为什么返回值是四个字节的 int 类型?

  这是为了考虑文件结束标志 EOF 的取值。EOF 取值是 -1,如果用 unsigned 类型的话,显然取不到 -1;而如果用 char 类型的话,则 -1 对应的值为 0xff(C 语言中,数值以补码形式存储),但是 0xff 又可以是一个字节的 ASCII 码值(一些扩展字符的 ASCII > 127溢出时,可能会产生值为 0xff 的ASCII 码),这样用 EOF 显然就不能判断文件是否结束了,因为会把 ASCII 码值为 0xff 的字节误判为文件结束符。如果将返回值用 int 类型来存放的话,那么 EOF(也就是 -1)将会被保存为 0xffffffff,这时在读到 0xff,用 int 类型进行存储的话,就是 0x000000ff,就不会和 EOF 相冲突了。

八、fputc、putc 和 putchar 函数

  fputc 函数把一个字符写到一个输出文件流中。它返回写入的值,如果失败,则返回 EOF。

#include <stdio.h>

int fputc(int c,FILE *stream);    // 需要注意的是,这里的 c 其实是字符,把字符当做 int 类型而不是 char 类型
int putc(int c,FILE *stream);
int putchar(int c);

  putc 函数的作用与 fputc 函数一样,但是它能被实现为一个宏。putchar 函数相当于 putc(c,stdout),它把单个字符写到标准输出。

  注意:putchar 和 getchar 都是把字符当做 int 类型而不是 char 类型来使用的,理由上面有讲。用一个例子演示一下 fgetc 和 fputc 函数的用法:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> int main()
{
int buf;
FILE *file = fopen("hello.txt","r");
if(file == NULL){
printf("fail to open the file!\n");
exit();
} while((buf = fgetc(file)) != EOF){
fputc(buf,stdout);
}
fclose(file); exit();
} /* 输出结果:
hello,my name is tongye!
2018.10.26
*/

  这段代码打开文件 hello.txt,并使用 fgetc 函数将文件中的内容一个字节一个字节地取出,同时使用 fputc 函数将该字节写到标准输出流中,直到文件结束。

九、fgets 和 gets 函数

  fgets 函数从输入文件流 stream 里读取一个字符串,并存放到缓冲区中,一次读取的字符个数有限制。

#include <stdio.h>

char *fgets(char *s,int n,FILE *stream);
char *gets(char *s);

  fgets 函数把读到的字符写到 s 指向的字符串里,直到:

1)遇到换行符,则停止读入字符,并将遇到的换行符一起传递给接收字符串,再加上一个表示结尾的空字节 \0;

2)已经传输了 n-1 个字符,则加上一个空字节 \0 结尾后,停止读入字符;

3)到达文件尾(EOF)。

  当成功调用函数时,fgets 返回一个指向字符串 s 的指针。如果文件流已经到达文件尾,fgets 会设置这个文件流的 EOF 标识并返回一个空指针;如果调用函数出错,则 fgets 返回一个空指针并设置 errno 以指出错误的类型。

  gets 函数类似于 fgets,只不过它直接从标准输入(stdin)读取数据并丢弃了遇到的换行符,它在接收字符串的尾部加上一个 null 字节、另外,需要注意的是,gets 函数并没有对传输字符的个数做限制,所以它可能会溢出自己的传输缓冲区。因此,一般来说,推荐使用 fgets 函数来替代 gets 函数。

参考资料:

《Linux程序设计 第四版》

Linux-C语言标准输入输出的更多相关文章

  1. Linux学习之输入输出重定向

    转自:http://www.cnblogs.com/chengmo/archive/2010/10/20/1855805.html 多谢分享 在了解重定向之前,我们先来看看linux 的文件描述符. ...

  2. Linux基础之输入输出

    第十五章 输入输出 一.重定向概述 1.1.什么是重定向? 将原本要输出到屏幕的数据信息,重新定向到指定的文件中. 比如:每天凌晨定时备份数据,希望将备份数据的结果保存到某个文件中.这样第二天通过查看 ...

  3. LINUX nohup命令输入输出深浅进出

    无论是否将 nohup命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中.如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中 ...

  4. linux kernel下输入输出console怎样实现

    近期工作在调试usb虚拟串口,让其作为kernel启动的调试串口,以及user空间的输入输出控制台. 利用这个机会,学习下printk怎样选择往哪个console输出以及user空间下控制台怎样选择. ...

  5. (四)Linux Shell编程——输入输出重定向

    Unix 命令默认从标准输入设备(stdin)获取输入,将结果输出到标准输出设备(stdout)显示.一般情况下,标准输入设备就是键盘,标准输出设备就是终端,即显示器. 1. 输出重定向 命令的输出不 ...

  6. linux kernel下输入输出console如何实现【转】

    转自:https://blog.csdn.net/skyflying2012/article/details/41078349 最近工作在调试usb虚拟串口,让其作为kernel启动的调试串口,以及u ...

  7. Linux shell的输入输出

    echo --echo命令可以显示文本行或变量,或者把字符串输入到文件 --echo [option] string -e 解析转义字符 例如:echo -e "nimenhao\nasfd ...

  8. linux输入输出、重定向、管道

    本篇讲述linux系统的输入输出.管道和重定向. 1. liunx的输入输出 一个linux系统要想发挥作用,就要有输入输出,这样才可以与外界交互. 类型 设备文件名 文件描述符 设备名称 说明 备注 ...

  9. Linux中的工作管理(Job Control )

    以前使用Linux老是会不小心按下Ctrl + z,然后就出现看不懂的情况,以为程序突然就没了,今天专门研究了下Linux下的几个快捷键和工作管理. 其中找到一篇很不错的文章,大部分是里面转载的. 原 ...

随机推荐

  1. mac 安装npm

    npm是什么 NPM的全称是Node Package Manager ,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准. 如何安装 一:如果你安装了Homebrew ...

  2. ROS——rqt

    $ rosrun rqt_plot rqt_plot   # 画出发布在topic上的数据变化图 $ rosrun rqt_graph rqt_graph #画出node关系图 $ rosrun rq ...

  3. c++——静态成员变量成员函数

    静态成员变量成员函数 思考:每个变量,拥有属性.有没有一些属性,归所有对象拥有? 4.1静态成员变量 1)定义静态成员变量 关键字 static 可以用于说明一个类的成员, 静态成员提供了一个同类对象 ...

  4. ajax和原生ajax、文件的上传

    ajax理解: ajax发送的请求是异步处理的.也就是说如下形式: function f1(){ $.ajax( { ....... success:function(){ a= return a } ...

  5. 51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)

    1107 斜率小于0的连线数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题   二维平面上N个点之间共有C(n,2)条连线.求这C(n,2)条线中斜率小于0的线 ...

  6. 很清晰的解读i2c协议

    很清晰的解读i2c协议 转载:http://dpinglee.blog.163.com/blog/static/14409775320112239374615/ 1.I2C协议 2条双向串行线,一条数 ...

  7. TarsGo新版本发布,支持protobuf,zipkin和自定义插件

    本文作者:陈明杰(sandyskies) Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架,目前支持C++,Java,PHP,Nodejs,Golang语言.该框架为用户提供了涉及 ...

  8. oracle 判断字段相等,但类型不同引起的性能问题

    最近做ogg数据同步,然后触发器加工数据放入另外一张表,由于数据量很大,一分钟几万条数据,由于一些条件字段类型不匹配,引起ogg阻塞,比较头大.最后分析发现性能问题.请看下图: phmxxh是varc ...

  9. CPP strcat函数使用

    strcat函数原型 char * strcat ( char * destination, const char * source ); strcat常见写法 // main.cpp // 字符数组 ...

  10. Angular4 自制打地鼠游戏

    前端工程师新手一枚,之前一直做些小设计,以及静态页面的编写工作.刚刚接触 Angular 没有多久,四个月前对于 Javascript也只是会写 alert 之流,现在进步算是很大,下面是自制的打地鼠 ...