`

Linux系统编程学习笔记(二)C标准I/O库

阅读更多
标准I/O
Buffered I/O:
Buffered I/O可以减少系统调用的次数,提高性能。每次读一个block的整数倍可以提高效率。
1、Standard I/O:
C语言的标准IO库stdio,提供了跨平台的,user-buffering的方案。
2、文件指针:
标准I/O不直接操作文件描述符,而是使用文件指针来操作文件。
FILE *fp
打开的文件被称为流,输入流、输出流、输入/输出流
3、打开文件:
#include <stdio.h>

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

mode描述了如何打开由path代表的文件:
r:打开文件,可读,流的位置在文件的开头
r+:打开文件,可读写,流的位置在文件的开头
w: 打开文件,可写,如果存在,清空。不存在创建,流的位置在文件的开头
w+:打开文件,可读写,如果存在,则清空,如果不存在则创建,流的位置在文件的开头
a:append模式,可写,如果不存在创建。流的位置在文件的末尾。
a+:append模式,可读写,如果不存在创建。流的位置在文件的末尾。
例子:
File *fp;

fp = fopen("/etc/manifest","r");
if(! fp )
	/* error */

4、通过文件 描述符打开:
#include <stdio.h>

FILE *fdopen(int fd, const char *mode);

mode的含义和fopen一样,除了w、w+不会清空文件。
例子:
FILE *fp;
int fd;

fd = open("/home/kidd/map.txt",O_RDONLY);
if(fd == -1)
  /* error */
fp = fdopen(fd,"r");
if(!fp)
 /* error */

一个可读的fd,如果fdopen设置mode是w,会如何?二者不一致会返回NULL,并设置
errno为EIVAL.

5、关闭流:
fclose可以关闭文件。
#include <stdio.h>

int fclose(FILE *fp);

buffer和没有写的数据首先被fushed。成功返回0,失败返回EOF。
6、关闭所有的流:
fcloseall关闭当前进程所有的流:
#define _GNU_SOURCE

#include<stdio.h>

int fcloseall(void);

总是返回0。
7、从流中读:
1)一次读一个字符:
fgetc:
#include <stdio.h>

int fgetc(FILE *fp);

返回读取字符转换成了int值,主要由于考虑文件结尾返回EOF,-1不在assci码范围。
int c;
while((c = fgetc(fp) != EOF){
	printf("%c\n",(char)c);
}

2)将字符压回流:
ungetc:
通过这个函数可以peek流中的字符,如果读超过了需要的字符,可以把
这个字符压回。
#include <stdio.h>

int ungetc(int c, FILE *fp);

成功返回c,失败返回EOF
3)读取一行:
fgets可以读取一行字符串:
#include <stdio.h>

char * fgets(char *str,int size, FILE *stream);

读取遇到换行或者EOF,成功返回str,失败返回NULL。
例子:
char buf[LINE_MAX];//LINE_MAX in <limits.h>du
if(!fgets(buf,LINE_MAX,fp)
	/* error */

4)读二进制数:
fread:
#include <stdio.h>

size_t fread(void *buf, size_t size, size_t nr, FILE *fp);

可以读取复杂的二进制数,比如c中的结构体。
读nr个数据,每一个大小为size字节。成功返回读取元素的个数,失败指示错误或者返回EOF,
但是如果不使用ferror()和feof()无法区分。
由于在不同机器的变量大小,对其,补齐,字节序不同,不具有跨机器性。
例子:
char buf[64];
size_t nr;

nr = fread(buf,sizeof(buf),1,fp);
if(nr == 0) 
	/* error */

8、写入流:
1)写入一个字符:
#include <stdio.h>

int fputc(int c, FILE *fp);

成功返回c,失败返回EOF。
if( fputc('p',fp) ) 
  /* error */

2)写入一个字符串:
fputs:
#include <stdio.h>

int fputs(const char *str, FILE *stream);

2)写入由\0结尾的字符串,成功返回非负数,失败返回EOF。
例子:
FILE *fp;

if(!(fp = fopen("journal.txt","a"))
  /* error */
if(fputs("The ship is made of wood.\n",fp) == EOF)
 /*error*/
if(fclose(fp) == EOF)
 /*error*/

3)写入二进制数据:
fwrtie可以将复杂的结构数据存储到文件中:
#include <stdio.h>

size_t fwrite(void *buf,size_t size, size_t nr, FILE *fp);

将右buf指向的数据写入文件fp中nr个元素,每一个元素的长度是size字节。
9、使用Buffered I/O 例子:
#include <stdio.h>

int main(void){
	FILE *in,*out;
	struct pirate{
		char name[100];
		unsigned long booty;
		unsigned int beard_len;
	}p,blackbeard = {"Edward Teach",950,48};
	
	out = fopen("data","w");

	if(! out ){
		perror("fopen");
		return 1;
	}

	if(!fwrite(&blackbeard,sizeof(struct pirate),1,out)){
		perror("fwrite");
		return 1;
	}

	if(fclose(out)){
		perror("fclose");
		return 1;
	}

	in = fopen("data","r");
	if( !in ){
		perror("fopen");
		return 1;
	}

	if(! fread(&p, sizeof(struct pirate),1,in)){
		perror("fread");
		return 1;
	}

	if(fclose(in)){
		perror("fclose");
		return 1;
	}

	printf("name=\"%s\" booty=%lu beard_len=%u\n",p.name,p.booty,p.beard_len);
	
	return 0;
}

10、Seeking a Stream
1)fseek和标准IO lseek类似:
#include <stdio.h>

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

whence可以取:SEEK_SET、SEEK_CUR,SEEK_END,和lseek含义一样。
2)fsetpos:
#include <stdio.h>

int fsetpos(FILE *fp,fpos_t *pos);

和fseek将whence设置成SEEK_SET一样。
3)rewind:
将文件位置重置,相当于fseek(fp,0,SEEK_SET);
#include <stdio.h>

void rewind(FILE *fp);

rewind 没有返回值,可以通过errno来判断是否成功:
errno = 0;
rewind(stream);
if(errno)
  /* error */

11、获取当前文件流的位置:
1)ftell:
#include <stdio.h>

long ftell(FILE *stream);

成功返回位置,失败返回-1,errno被设置。
2)fgetpos:
#include <stdio.h>

int fgetpos(FILE *stream, fpos_t *pos);

成功返回0,并设置pos,失败返回-1。
12、flush a Stream:
fflush:
#include <stdio.h>

int fflush(FILE *fp);

所有由fp标示的未写入的数据写入内核的buffer中。
成功返回0,失败返回EOF,并设置errno
write都会写入用户的buffer中,fflush可以将用户buffer的数据,写入内核buffer中,
并不能保证写入磁盘。
13、错误和文件结尾:
一些标准的I/O比如fread,没有提供区分error和EOF的机制,所以检查所给的流确定遇到错误还是文件
结尾很有用。标准I/O提供了两个接口:
1)ferror:
#include <stdio.h>

int ferror(FILE *fp);

返回非0表示设置了错误,0表示没有。
2)feof
#include <stdio.h>

int feof(FILE *fp);

测试流是否设置了EOF,返回非零表示设置,0表示没有。
3)清除错误和EOF设置:
#include<stdio.h>

void clearerr(FILE *fp);

例子:
if(ferror(f))
	printf("Error on f!\n");
if(feof(f))
	printf("EOF on f!\n");
clearerr(f);

14、获得相关的文件描述符:
#include <stdio.h>

int fileno(FILE *fp);

成功返回相关的文件描述符,失败返回-1
混用标准I/O和IO系统调用并不是值得推荐的。
15、控制buffer:
a、标准I/O实现了三种用户Buffer:
1)Unbuffered:
没有缓存,数据直接交给内核。stderr默认使用这种方式。
2)Line-buffered:
buffer缓存一行,当遇到换行符则提交给内核。stdout默认使用这种方式。
3)Block-buffered
与文件相关的I/O默认都是Block-buffered。
b、设置流的buffer类型:
setvbuf:
#include <stdio.h>

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

mode:
_IONBF: 没有buffer
_IOLBF: Line-buffered
_IOFBF: block-buffered/full buffered
除了_IONBF外,其他的两个,都需要用户提供一个size自己的buf。
提供的buffer在文件关闭的时候仍然要可见,否则显示的关闭操作会失败。
下面例子存在bug:
#include <stdio.h>

int main(void){
	char buf[BUFSIZ];
	
	setvbuf(stdout,buf,_IOFBF,BUFSIZ);
	printf("Arrr!\n");

	return 0;
}

程序退出的时候会隐式的关闭文件,导致失败。可以在上面程序return之前加上显示的close关闭stdout,或者
使用全局的buf。

16、格式化I/O:
1)使用可变参数族:
#include <stdio.h>

//正确返回输出的字节数,错误返回-1
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char * restrict format,...);

//调用者需要确保buf足够大,不会溢出,
int sprintf(char *restrict buf,const char * restrict format,...);
//显示指定了buf的大小,多出来的会被抛弃
int snprintf(char *restrict buf, size_t n, const char *restrict format,...);
//正确返回输出到数组buf中的字节数,错误返回-1。

2)使用va_list族:
va_list在<stdarg.h>中定义。
#include <stdio.h>
#include <stdarg.h>

int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char * restrict format,va_list arg);
int vsprintf(char *restrict buf,const char * restrict format,va_list arg);
int vsnprintf(char *restrict buf, size_t n, const char *restrict format,va_list arg);

17、格式化输入:
1)使用可变参数族:
#include <stdio.h>

int scanf(const char *restrict format,...);
int fscanf(FILE * restrict fp, const char * restrict format,...);
int sscanf(const char *restrict buf, const char *restrict format,...);

2)使用va_list族
#include <stdio.h>
#include <stdarg.h>

int vscanf(const char *restrict format,va_list arg);
int vfscanf(FILE * restrict fp, const char * restrict format,va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format,va_list arg);


参考:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》
0
1
分享到:
评论

相关推荐

    深入探究文件I/O-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

    深入探究文件 I/O:深入了解了文件 I/O 中的一些细节,譬如文件的管理方式、 错误返回的处理、空洞文件、O_APPEND 和 O_TRUNC 标志、原子操作与竞争冒险等等

    Linux网络编程socket编程学习

    自学Linux网络编程关于socket的编写,包括 server.c 和 client.c 的编写;很详细的介绍了网络套接字socket的C/S模型TCP协议的服务器端和客户端的程序函数以及编写过程;重点介绍多路I/O转接服务器的实现,包括select...

    c#学习笔记.txt

    c#学习笔记(1) 51099在线学习网发布 文章来源:网络收集 发布时间:2006-05-25 字体: [大 中 小] 51099在线学习网 http://www.51099.com 1, 结构(struct) 与 类(class) [attributes] [modifiers] struct ...

    CSAPP(深入理解计算机系统)

    本书的主要论题包括:数据表示、C程序的机器级表示、处理器结构,程序优化、存储器层次结构、链接、异常控制流、虚拟存储器和存储器管理、系统级I/O、网络编程和并发编程。书中所覆盖的内容主要是这些方面是如何影响...

    深入理解计算机系统(英文版)

    本书的主要论题包括:数据表示、C程序的机器级表示、处理器结构,程序优化、存储器层次结构、链接、异常控制流、虚拟存储器和存储器管理、系统级I/O、网络编程和并发编程。书中所覆盖的内容主要是这些方面是如何影响...

    Embedded-Linux-Development:学习记录

    嵌入式 Linux 开发学习笔记 应用开发 第一章:环境搭建 第二章:文件 I/O 编程 第三章:多人物编程 第四章:进程间通信 第五章:多线程编程 第六章:网络编程 驱动开发

    自己动手写操作系统(含源代码).part2

    如果考虑到操作系统作为软硬件桥梁的特殊地位,那么它可能看上去比一般的软件系统更难理解,因为其核心部分往往包含许多直接针对CPU、内存和 I/O端口的操作,它们夹杂在一片代码汪洋之中,显得更加晦涩。 我们有许多...

    自己动手写操作系统(含源代码).part1

    如果考虑到操作系统作为软硬件桥梁的特殊地位,那么它可能看上去比一般的软件系统更难理解,因为其核心部分往往包含许多直接针对CPU、内存和 I/O端口的操作,它们夹杂在一片代码汪洋之中,显得更加晦涩。 我们有许多...

    产品经理刷leetcode-AlgorithmExercises:我的算法练习和笔记

    I/O 设备管理 文件系统 linux指令 数据库系统 MySQL 新兴领域概念复述 高级数据库+hivehttps:// 数据挖掘笔记 机器学习 深度学习 云计算 产品经理 B站课程 微博分析 牛客上的题 自我介绍 参考资料 CS技术面试基础...

    基于Opencv框架和MFC相结合的小程序,操作笔记本电脑摄像头,完成拍照摄像图片渲染等功能.zip

    多语言支持:尽管OpenCV主要使用C++编写,但它提供了丰富的API绑定,支持包括C、Python、Java、MATLAB、JavaScript等多种编程语言,方便不同领域的开发者使用。 开源与免费:OpenCV遵循BSD开源许可证发布,用户...

Global site tag (gtag.js) - Google Analytics