顺序读写文件
文件打开后可以对其进行读写操作,打开时需要指定相应的打开模式)。读写操作分为顺序读写操作和随机读写操作。顺序读写操作是指打开文件之后从文件头开始读写,即读写数据的顺序和数据在文件中的存储顺序是一致的。
读写单个字符
从文件读取单个字符可以使用fgetc和getc函数。这两个函数的作用都是从文件流中读取下一个字符并推进文件的位置指示器(用来指示要读写的下一一个字符的位置)。
#include <stdio.h>
...
int fgetc (FILE *stream) ;
int getc(FILE *stream) ;
参数是一个FILE结构的指针,指定一个待读取的文件流。如果读取成功,该函数将读取到的unsigned char 类型转换为int类型并返回:如果文件结束或者遇到错误则返回EOF。
fgetc和getc 的功能和描述基本上是一样的, 它们的区别主要在于实现: fgetc 是个函数:而getc实际上是一个宏的实现。一殷来说,宏虽产生较大的代码量,但是避免了函数调用的堆栈操作,所以速度会比较快。由于getc 是由宏实现的,对其参数可能有不止一次的调用,所以不能使用带有副作用(side efects)的参数(所谓副作用,其实就是参数中不能存在自增、自减运算符,因为宏在展开后可能参数会被多次引用。
向文件写入单个字符可以使用fputc和pute函数。
#include <stdio.h>
...
int fputc(int c, FILE *stream) ;
int putc(int CFILE *stream) ;
与上面一样,它们的区别是: fputce 是个函数, 而pute则是 个宏的实现。 它们都有两个参数:第一个参数指定待写入的字符:第二个参数是一个FILE结构的指针,指定一个待写入的文件流。


读写整个字符串
从文件读整个字符串和向文件写整个字符串可以使用fgets 和fputs 函数。
#include <stdio.h>
...
char *fgets (char *S,int size, FILE *stream) ;
int fputs (const char *s, FILE *stream) ;
其中,fgets 函数用于从指定文件中读取字符串。fgets 函数最多可以读取size -1个字符,因为结尾处会自动添加个字符串结束符 (^0)。当读取到换行符 ("n')或文件结束符(EOF)时,表示结束读取("n'会被作为个合法的字符读取)。
fgets函数有三个参数:第一个参数是一个字符型指针,指向用于存放读取到的字符串的位置;第二个参数指定读取的字符数(包括最后自动添加的"0):最后一个参数是一个FILE结构的指针,指定一个待读取的文件流。
如果函数调用成功,返回第一个参数指向的地址。如果在读取字符的过程中遇到EOF,则eof指示器被设置:如果还没读入任何字符就遇到EOF,则第一个参数指向的位置保持原来的内容,函数返回NULL;如果在读取的过程中发生错误,则error指示器被设置,函数返回NULL,但第一个参数指向的内容可能被改变。
将一个字符串写入文件中使用fpus 函数。注意,字符串结束符("0)不会被一并写入。
fputs函数有两个参数:第一个参数是一个字符型指针,指向用于存放待写入字符串的位置:第二个参数是一个FILE结构的指针,指定一个待操作的数据流。如果函数调用成功,返回一个非0值:如果函数调用失败,返回EOF。
举个例子调用多次fpus函数将几个字符串写入文件中,然后调用fgets 函数读取并打印到屏幕上


feof函数用于检测eof指示器是否被设置,如果检测到文件末尾指示器被设置,返回一个非0值:否则返回0。
第三行字符串打印了两次,但是查看lines.txt 文件的内容却是正确的:

分析:这是因为fgets 函数一旦遇到换行符("n')就会停止本次字符串的读取,而在最后一行字符串读取完成之后,并没有遇到EOF (是换行符导致了本次读取结束),所以它还要多读取一次,但是这一次除了读取到EOF就没有其他内容了,因此buffer字符串的内容并没有被新的内容所覆盖。
格式化读写文件
scanf和printf函数使用它们可以对终端进行格式化字符串输入和输出。同样,对于文件的读写,也有两个类似的函数— fscanf 和fprintf函数。
#include <stdio.h>
...
int fscanf (FILE *stream, const char *format, .. ) ;
int fprintf (FILE *stream, const char*format, .. .) ;
从名字上看,它们只是在scanf和printf前面加上一个表示文件的f (file); 从用法上看,fscanf 和fprintf函数的读写对象是文件而不是终端,其中的format 参数和附加参数用法与scanf和printf函数是一致的。


二进制读写文件
虽然fopen函数可以指定以何种模式(文本模式或二进制模式)打开一个文件,但这并不意味着后续对该文件的操作就定是对应的形式。


代码中,虽然使用"wb"二进制模式打开了一个text.txt文件,但仍然调用fputc函数成功地将四个字符('51、12'、 '0'和\n') 写入了文件中。使用xxd命令以十六进制的形式查看该文件内容,35、32、30和0a分别对应的是'5'、'2'、 '0'和"\n'的 ASCII编码。
C语言为直接读写文件提供了fread 和fwrite函数。
#include <stdio.h>
size_t fread(void*ptr, size_t size,size_t nmemb, FILE *stream) ;
size_t fwrite (const void *ptr,size_t size,size_t nmemb, FILE *stream);
其中,fread 函数用于从指定文件中读取指定尺寸的数据。该函数共有四个参数:第一个参数指向存放数据的内存块地址:第二个参数指定待读取的每个元素的尺寸:第三个参数指定待读取的元素个数:最后一个参数是一个FILE结构的指针,指向一个待读取的文件流。



既然是使用文本模式打开的文件,又能够正常地将内容打印到屏幕上,那么file.xt文件中的内容定应该是文本。 使用vi命令打开file.xt文件来看看

这是典型的二进制数据,因此使用文本编辑器并不能读取其内容。
不难得出结论:不管是用文本模式还是用二进制模式打开文件,都不能决定写入数据的形式,它们只是影响换行符的表现形式而已。真正决定数据是以字符的形式写入还是以二进制的形式写入,则是相关的文件读写函数。