第九章 I/O 库函数
系统调用函数:open() read() write() lseek() close();
I/O库函数:fopen() fread()、fwrite() fseek() fclose()。
I/O库函数的算法
fread算法
最开始,FILE结构体的内部缓冲区是空的。在每次调用fread()时,它将数据写人内部缓冲区,并初始化缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。直到满足字节数。
fwrite算法
fwrite()算法与fread()算法只是数据传输方向不同。最开始,FILE结构体的内部缓冲区是空的。在每次调用fwrite()时,它将数据写人内部缓冲区,并调整缓冲区的指针、计数器和状态变量,以跟踪缓冲区中的字节数。如果缓冲区已满,则发出write()系统调用,将整个缓冲区写人操作系统内核。
fclose算法
若文件以写的方式被打开,fclose()会先关闭文件流的局部缓冲区。然后,它会发出一个close(fd)系统调用来关闭FILE结构体中的文件描述符。最后,它会释放FILE结构体,并将FILE指针重置为NULL。
I/O库模式
fopen()
- r:以只读方式打开文件,该文件必须存在。
- r+:以读/写方式打开文件,该文件必须存在。
- rb+:以读/写方式打开一个二进制文件,只允许读/写数据。
- rt+:以读/写方式打开一个文本文件,允许读和写。
- w:打开只写文件,若文件存在则长度清为0,即该文件内容消失,若不存在则创建该文件。
- w+:打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
- a:以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF符保留)。
- a+:以附加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符 不保留)。
- wb:以只写方式打开或新建一个二进制文件,只允许写数据。
- wb+:以读/写方式打开或建立一个二进制文件,允许读和写。
- wt+:以读/写方式打开或建立一个文本文件,允许读写。
- at+:以读/写方式打开一个文本文件,允许读或在文本末追加数据。
- ab+:以读/写方式打开一个二进制文件,允许读或在文件末追加数据。
fgetc()
C 库函数 int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动
返回值
该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF。
#include <stdio.h>
int main ()
{
FILE *fp;
int c;
int n = 0;
fp = fopen("file.txt","r");
if(fp == NULL)
{
perror("打开文件时发生错误");
return(-1);
}
do
{
c = fgetc(fp);
if( feof(fp) )
{
break ;
}
printf("%c", c);
}while(1);
fclose(fp);
return(0);
}
练习编写一个C程序,将文本文件中的字母由小写转换成大写
#include <stdio.h>
int main ()
{
FILE *fp;
int c;
int n = 0;
fp = fopen("file.txt","r");
if(fp == NULL)
{
perror("打开文件时发生错误");
return(-1);
}
do
{
c = fgetc(fp);
if( feof(fp) )
{
break ;
}
if(c>96&&c<=122) c=c-32;
printf("%c", c);
}while(1);
fclose(fp);
return(0);
}
行模式I/O
char *fgets(char *bufintsizeFILE*fp):从fp中读取最多为一行(以\n结尾)的字符。
intfputs(char*buf,FILE*fp):将buf中的一行写人fp中。
格式化I/O
格式化输入(注意类比思维)
scanf(char *FMT,&items);
fscanf(fp, char *FMT,&items);
格式化输出
printf(char *FMT,items);
fprintf(fp,char *FMT,items)
内存中的转换函数
sscanf(buf,FMT,&items);
sprintf(buf, FMT, items);
其他/O库函数
- fseek() 、ftell()、 rewind():更改文件流中的读/写字节位置。
- feof() 、ferr()、fileno():测试文件流状态。
- fdopen():用文件描述符打开文件流。
- freopen():以新名称重新打开现有的流。
- setbufo ()、setvbuf:设置缓冲方案。
- popen():创建管道,复刻子进程来调用sh。
限制混合fread-fwrite
当某文件同时进行读、写操作时,会限制混合使用fread()和fwrite(),注意两者之间至少有一个fseek()或者ftell()
文件流缓冲
每个文件流都有一个FILE结构体,其中包含一个内部缓冲区。对文件流进行读写需要遍历FILE结构体的内部缓冲区。文件流可以使用三种缓冲方案中的一种。
- 无缓冲:从非缓冲流中写人或读取的字符将尽快单独传输到文件或从文件中传输。
- 行缓冲:遇到换行符时,写人行缓冲流的字符以块的形式传输。例如,文件流stdout通常是行缓冲,逐行输出数据。
- 全缓冲:写人全缓冲流或从中读取的字符以块大小传输到文件或从文件传输。这是文件流的正常缓冲方案。
变参函数
在I/O库函数中,printf()相当独特,因为多种不同类型的可变数量参数可以调用它。这是允许的,因为最初的C语言不是一种类型检查语言。目前,C语言和C + +会强执行类型检查,但是为了方便,这两种语言仍然允许参数数量可变的函数。这些函数必须至少个一个参数进行声明,后跟3个点,如:
int func(int m, int n...) //n=last specified parameter
在函数内部,可以通过C语言库宏访问参数:
enter code hervoid 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
问题:运行此程序没有出现报错却没有输出
#include <stdio.h>
int main ()
{
FILE *fp;
int c,a;
int n = 0;
char buf[1024];
fp = fopen("file.txt","r+");
fread(buf,1,100,fp);
fseek(fp,(long)39,0);
fwrite(buf,1,39,fp);
}