《Unix&Linux系统编程》第九章学习笔记
I/O库函数 知识点总结
9.1 I/O库函数
系统调用是文件操作的基础,只支持数据块的读/写,不支持逻辑单元。
I/O库函数是一系列文件操作函数,方便使用,提高效率。
9.2 I/O库函数与系统调用
系统调用函数:open()、read()、write()、lseek()、close();
I/O库函数:fopen()、fread()、fwrite()、fseek()、fclose()。
fopen()依赖于open(),fread()依赖于read()。
9.3 I/O库函数的算法
fread()算法:
fread()一面接收来自用户程序的调用,一面面向操作系统内核发出read()系统调用。
fwrite()算法:
与fread算法类似,但传输方式不同。结构体缓冲区为空,调用fwrite()时,将数据写入内部缓冲区,调整缓冲区指针、计数器、状态变量,跟踪缓冲区字节数。若结构体缓冲区已满,发出write()系统调用,将整个缓冲区写入操作系统内核。
fclose()算法:
文件以写的方式打开,fclose()先关闭文件流的局部缓冲区,发出close(fd)系统调用来关闭FILE结构体中文件描述符,最终释放结构体,重置指针为NULL。
9.5 使用I/O库函数或系统调用
fopen()中的模式参数可以指定为:"r"、"w"、"a",分别代表读、写、追加。
每个模式字符串可包含一个+号,表示同时读写,或者在写入、追加情况下,如果文件不存在则创建文件。
"r+":表示读/写,不会截断文件。
"w+":表示读/写,但是会先截断文件;如果文件不存在,会创建文件。
"a+":表示通过追加进行读/写;如果文件不存在,会创建文件。
9.5.1 字符模式I/O
int fgetc(FILE *fp); //get a char from fp, cast to int
int ungetc(int c, FILE *fp); //push a previously char got by fgetc() back to stream
int fput(int c, FILE *fp); //put a char to fp
注意,fgetc()返回的是整数,而不是字符。这是因为它必须在文件结束时返回文件结束符。文件结束符通常是一个整数-1,将它与文件流中的任何字符区分开。
对于fp=stdin或stdout,可能会使用c=getchar() ; putchar(c);来代替。对于运行时效来说,getchar() 和putchar() 通常不是getc() 和 putc()的缩小版本。相反,可以将它们实现为宏,以避免额外的函数调用。
9.5.5 其他I/O库函数
其他I/O库函数 | |
---|---|
fseek()、ftell()、rewind() | 更改文件流中的读/写字节位置 |
feof()、ferr()、fileno() | 测试文件流状态 |
fdopen() | 用文件描述符打开文件流 |
freopen() | 以新名称重新打开现有的流 |
setbuf()、setvbuf() | 设置缓冲方案 |
popen() | 创建管道,复刻子进程来调用sh |
9.5.6 限制混合fread-fwrite
规范要求每对fread()和fwrite()之间至少有一个fseek()或ftell()。
9.6 文件流缓冲
无缓冲:从非缓冲流中写入或读取的字符将尽快单独传输到文件或从文件中传输。例如,文件流stderr通常无缓冲。到stderr的所有输出都会立即发出。
行缓冲:遇到换行符时,写入行缓冲流的字符以块的形式传输。例如,文件流stdout通常是行缓冲,逐行输出数据。
全缓冲:写入全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文件流的正常缓冲方案。
通过fopen()创建文件流之后,在对其执行任何操作之前,用户均可发出一个
setvbuf(FTLE *stream,char *buf, int node, int size)
调用来设置缓冲区(buf)、缓冲区大小(size)和缓冲方案(mode),它们必须是以下一个宏:
- _IONBUF:无缓冲。
- _IOLBUF:行缓冲。
- _IOFBUF:全缓冲。
对于行缓冲流或全缓冲流,可用fflush(stream)立即清除流的缓冲区。
9.7 变参函数
目前,C语言和C++会强制执行类型检查,但这两种语言仍然允许参数数量可变的函数。这些函数必须至少使用一个参数进行声明,后跟3个点,如
int func(int m, int n ...) //n = last specified parameter
在函数内部,可以通过C语言库宏访问参数:
void va_start(va_list ap,last); // start param list from last parameter
type va_arg(va_list ap, type); // type = next parameter type
va_end(va_list ap); // clear parameter list
问题与解答
1. 文件操作都有什么?
创建、打开、关闭、读、写、搜索等。
创建:
FILE *fopen(const char *filename,const char *mode) //filename:文件名 mode:打开模式
常见的打开方式:
r,rb 只读
w,wb 只写,如果文件不存在就创建
a,ab 追加,如果文件不存在就创建
r+,r+b,rb+ 读写方式打开
w+,w+b,wh+ 读写方式打开,文件不存在则创建
a+,a+b,ab+ 读和追加方式打开,文件不存在则创建
读文件
size_t fread(void *ptr,size_t size,size_t n,FILE *stream)
stream:源文件 n:读取字段数 size: 每个字段的字节数 ptr:目标字符数组
返回实际读取的字节数
写文件
size_t fwrite(const void *ptr,size_t size,size_t n,FILE *stream)
ptr:源数组 n:字段数 size:字段的字节数 stream:目标文件
返回实际写入字段数
2.二进制文件和文本文件如何转换?
标准I/O库中主要使用 fread/fwrite来读写二进制文件,而对于文本文件可以使用 fread/fwrite fgetc/fputc fprintf等,文本文件与二进制文件使用不同的编码方式,把一个数据以什么样的编码(字符还是值本身)存入文件是由用户主动选择的,也就是写入的接口选择,如果以二进制接口方式写入文件那么就是一个二进制文件,如果以字符方式写入文件就是一个文本文件了。既然有写入时候的编码也就会有读出的编码,只有两个编码对应才能读出正确的结果。
实践内容 代码与截图
-
(1)书本练习9.2:
将文本文件中的字母进行转换,由小写变成大写
-
源代码:
点击查看代码
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp; //将小写字母转换成大写,记录行数
int c;
FILE *gp;
int line=0;
if((fp = fopen("justdance.txt","a+"))==NULL||(gp = fopen("ustdance.txt","a+"))==NULL)
{
printf("申请失败!");
exit(0);
}
else
{
while((c=getc(fp))!=EOF)//fgetc()返回的是整数
{
if(c!=10)
{
putc(c-32,gp);
}
else
{
putc(c,gp);
line++;//回车数量也就是行数
}
}
fclose(fp);
fclose(gp);
}
if((fp = fopen("justdance.txt","w"))==NULL||(gp = fopen("ustdance.txt","r"))==NULL)
{
printf("申请失败!");
exit(0);
}
else
{
while((c=getc(gp))!=EOF)//fgetc()返回的是整数
{
putc(c,fp);
}
fclose(fp);
fclose(gp);
printf("大写更改成功!一共有%d行英文",line);
}
return 0;
}
- 输入文本文件内容为:
weihe
haha
wozaizheli
- 代码运行之后生成文本文件内容为:
WEIHE
HAHA
WOZAIZHELI
-
更改文本文件中的内容,
-
在终端上运行截图为:
-
(2)书本练习9.3:计算文本文件的行数
点击查看代码
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp; //将小写字母转换成大写,记录行数
int c;
int IsWords=0,i;//用来判断是否是单词
int Wordsnum=0;
char Words[20];
if((fp = fopen("justdance.txt","r"))==NULL)
{
printf("申请失败!");
exit(0);
}
else
{
while((c=getc(fp))!=EOF)//fgetc()返回的是整数
{
if((c>=97&&c<=122)||(c<=90&&c>=65))
{
Words[IsWords] = c;
IsWords++;
}
else
{
if(IsWords>0)
{
for( i=0;i<IsWords;i++) //将单词打印出来
{
printf("%c",Words[i]);
}
printf("\n");
Wordsnum++;
IsWords=0;
}
}
Wordsnum+=1;
printf("一共有%d个单词\n",Wordsnum);
fclose(fp);
return 0;
}
}