1. 文件操作
1.1. 文件概述
我们对文件的概念已经非常熟悉了,比如常见的 Word 文档、txt 文件、源文件等。文件是数据源的一种,最主要的作用是保存数据。
在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。例如:
通常把显示器称为标准输出文件,printf 就是向这个文件输出数据;
通常把键盘称为标准输入文件,scanf 就是从这个文件读取数据。
常见硬件设备所对应的文件
文件 | 硬件设备 |
---|---|
stdin | 标准输入文件,一般指键盘;scanf()、getchar() 等函数默认从 stdin 获取输入。 |
stdin | 标准输出文件,一般指显示器;printf()、putchar() 等函数默认向 stdout 输出数据。 |
stderr | 标准错误文件,一般指显示器;perror() 等函数默认向 stderr 输出数据(后续会讲到)。 |
stdprn | 标准打印文件,一般指打印机。 |
我们不去探讨硬件设备是如何被映射成文件的,大家只需要记住,在C语言中硬件设备可以看成文件,有些输入输出函数不需要你指明到底读写哪个文件,系统已经为它们设置了默认的文件,当然你也可以更改,例如让 printf 向磁盘上的文件输出数据。
1.1.1. 文件
操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。文件在进行读写操作之前要先打开,使用完毕要关闭。
所谓打开文件,就是获取文件的有关信息,例如文件名、文件状态、当前读写位置等,这些信息会被保存到一个 FILE 类型的结构体变量中。关闭文件就是断开与文件之间的联系,释放结构体变量,同时禁止再对该文件进行操作。
在C语言中,文件有多种读写方式,可以一个字符一个字符地读取,也可以读取一整行,还可以读取若干个字节。文件的读写位置也非常灵活,可以从文件开头读取,也可以从中间位置读取。
1.1.2. 文件流
所有的文件(保存在磁盘)都要载入内存才能处理,所有的数据必须写入文件(磁盘)才不会丢失。数据在文件和内存之间传递的过程叫做文件流,类似水从一个地方流动到另一个地方。数据从文件复制到内存的过程叫做输入流,从内存保存到文件的过程叫做输出流。
文件是数据源的一种,除了文件,还有数据库、网络、键盘等;数据传递到内存也就是保存到C语言的变量(例如整数、字符串、数组、缓冲区等)。我们把数据在数据源和程序(内存)之间传递的过程叫做数据流(Data Stream)。相应的,数据从数据源到程序(内存)的过程叫做输入流(Input Stream),从程序(内存)到数据源的过程叫做输出流(Output Stream)。
输入输出(Input output,IO)是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互的操作。几乎所有的程序都有输入与输出操作,如从键盘上读取数据,从本地或网络上的文件读取数据或写入数据等。通过输入和输出操作可以从外界接收信息,或者是把信息传递给外界。
我们可以说,打开文件就是打开了一个流。
1.2. 文件的打开函数fopen与关闭函数fclose
1.2.1. 文件的打开(fopen函数)
fopen() 函数用来打开一个文件,它的原型为:
FILE *fopen(char *filename, char *mode);
filename为文件名(包括文件路径),mode为打开方式,它们都是字符串。fopen() 会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个FILE类型的结构体变量中,然后将该变量的地址返回。
FILE是在stdio.h头文件中定义的一个结构体,用来保存文件信息。
如果希望接收 fopen() 的返回值,就需要定义一个 FILE 类型的指针。例如:
FILE *fp = fopen("demo.txt", "r");
表示以“只读”方式打开当前目录下的 demo.txt 文件,并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了。fp 通常被称为文件指针。又如:
FILE *fp = fopen("demo.txt","rb");
表示以二进制方式打开 D 盘下的 demo.txt 文件,允许读和写。
打开方式(mode)有多种,见下表:
打开方式 | 说明 |
---|---|
r | 以只读方式打开文件,只允许读取,不允许写入。该文件必须存在。 |
r+ | 以读/写方式打开文件,允许读取和写入。该文件必须存在。 |
rb+ | 以读/写方式打开一个二进制文件,允许读/写数据。 |
rt+ | 以读/写方式打开一个文本文件,允许读和写。 |
w | 以只写方式打开文件,若文件存在则长度清为0,即该文件内容消失,若不存在则创建该文件。 |
w+ | 以读/写方式打开文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。 |
a | 以追加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF符保留)。 |
a+ | 以追加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符 不保留)。 |
wb | 以只写方式打开或新建一个二进制文件,只允许写数据 |
wb+ | 以读/写方式打开或建立一个二进制文件,允许读和写。 |
wt+ | 以读/写方式打开或建立一个文本文件,允许读写。 |
at+ | 以读/写方式打开一个文本文件,允许读或在文本末追加数据。 |
ab+ | 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 |
几点说明
-
文件打开方式由r、w、a、t、b、+ 六个字符拼成,各字符的含义是:
r(read):读
w(write):写
a(append):追加
t(text):文本文件,可省略不写
b(banary):二进制文件
+:读和写 -
如果没有“b”字符,文件以文本方式打开。
-
凡用“r”打开一个文件时,该文件必须已经存在。
-
在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成打开文件的工作,并作相应的处理。因此常用以下程序段打开文件:
if( (fp=fopen("demo.txt","rb") == NULL ){
printf("Error on open demo.txt file!");
getch();
exit(1);
}
这段程序的意义是,如果返回的指针为空,表示不能打开D盘根目录下的 demo.txt 文件,并给出提示信息“error on open demo.txt file!”。第3行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里,该行的作用是等待,只有当用户从键盘敲任一键时,程序才继续执行,因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。
-
把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。
-
标准输入文件 stdin(键盘)、标准输出文件 stdout(显示器)、标准错误文件 stderr(显示器)是由系统打开的,可直接使用。
1.2.2. 文件关闭(fclose函数)
文件一旦使用完毕,应该用fclose()函数把文件关闭,以释放相关资源,避免数据丢失。fclose() 的原型为:
int fclose(FILE *fp);
fp 为文件指针。例如:
fclose(fp);
文件正常关闭时,fclose() 的返回值为0,如果返回非零值则表示有错误发生。
1.3. 文件的状态,包括feof函数和ferror函数;
对EOF的说明
EOF 本来表示文件末尾,意味着读取结束,但是很多函数在读取出错时也返回 EOF,那么当返回EOF时,到底是文件读取完毕了还是读取出错了?我们可以借助 stdio.h 中的两个函数来判断,分别是 feof() 和 ferror()。
1.3.1. feof()函数
feof() 函数用来判断文件内部指针是否指向了文件末尾,它的原型是:
int feof ( FILE * fp );
当指向文件末尾时返回非零值,否则返回零值。
1.3.2. ferror()函数
ferror() 函数用来判断文件操作是否出错,它的原型是:
int ferror ( FILE *fp );
出错时返回非零值,否则返回零值。
需要说明的是,文件出错是非常少见的情况,上面的示例基本能够保证将文件内的数据读取完毕。如果追求完美,也可以加上判断并给出提示:
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//如果文件不存在,给出提示并退出
if( (fp=fopen("demo.txt","rt")) == NULL ){
printf("Cannot open file, press any key to exit!");
getch();
exit(1);
}
//每次读取一个字节,直到读取完毕
while( (ch=fgetc(fp)) != EOF ){
putchar(ch);
}
if (!feof(fp) )
fprintf( stderr, "Error reading.\n" ); //perror("Error reading");
putchar('\n'); //输出换行符
if(ferror(fp)){
puts("Error reading");
}else{
puts("Success reading");
}
fclose(fp);
return 0;
}
1.4. 文件的读/写,包括fread和fwrite函数、fputc和fgetc函数、fgets与fputs函数的应用
在C语言中,读写文件比较灵活,既可以每次读写一个字符,也可以读写一个字符串,甚至是任意字节的数据(数据块)。
1.4.1. 以字符形式读写文件
以字符形式读写文件时,每次可以从文件中读取一个字符,或者向文件中写入一个字符。主要使用两个函数:fgetc()和fputc()。
1.4.1.1. 字符读取函数 fgetc
fgetc 是 file get char 的缩写,意思是从指定的文件中读取一个字符。它的原型为:
int fgetc (FILE *fp);
fp 为文件指针。fgetc() 读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回EOF。
EOF 是 end of file 的缩写,表示文件末尾,是在 stdio.h 中定义的宏,它的值是一个负数,往往是 -1。返回值类型之所以为 int,就是为了容纳这个负数(char不能是负数)。
注:EOF 不绝对是 -1,也可以是其他负数,这要看编译器的实现。
fgetc() 使用举例:
char ch;
FILE *fp = fopen("demo.txt", "r+");
ch = fgetc(fp);
表示从demo.txt文件中读取一个字符,并保存到变量ch中。
在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用fgetc 函数后,该指针会向后移动一个字节,所以可以连续多次使用fgetc读取多个字符。
注意:这个文件内部的位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,也就是读写到第几个字节,它不表示地址。文件每读写一次,位置指针就会移动一次,它不需要你在程序中定义和赋值,而是由系统自动设置,对用户是透明的。
1.4.1.2. 字符写入函数fputc
fputc 是 file output char 的所以,意思是向指定的文件中写入一个字符。调用的形式为:
int fputc ( int ch, FILE *fp );
ch 为要写入的字符,fp 为文件指针。fputc() 写入成功时返回写入的字符,失败时返回EOF,返回值类型为 int 也是为了容纳这个负数。例如:
fputc('a', fp);
或者:
char ch = 'a'; fputc(ch, fp);
表示把字符 ‘a’ 写入fp所指向的文件中。
两点说明
-
被写入的文件可以用写、读写、追加方式打开,用写或读写方式打开一个已存在的文件时将清除原有的文件内容,并将写入的字符放在文件开头。如需保留原有文件内容,并把写入的字符放在文件末尾,就必须以追加方式打开文件。不管以何种方式打开,被写入的文件若不存在时则创建该文件。
-
每写入一个字符,文件内部位置指针向后移动一个字节。
【示例】从键盘输入一行字符,写入文件。
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//判断文件是否成功打开
if( (fp=fopen("demo.txt","wt+")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
printf("Input a string:\n");
//每次从键盘读取一个字符并写入文件
while ( (ch=getchar()) != '\n' ){
fputc(ch,fp);
}
fclose(fp);
return 0;
}
运行程序,输入一行字符并按回车键结束,打开D盘下的demo.txt文件,就可以看到刚才输入的内容。
程序每次从键盘读取一个字符并写入文件,直到按下回车键,while 条件不成立,结束读取。
1.4.2. 以字符串形式读写文件
fgetc() 和 fputc() 函数每次只能读写一个字符,速度较慢;实际开发中往往是每次读写一个字符串或者一个数据块,这样能明显提高效率。
1.4.2.1. 读字符串函数fgets
fgets() 函数用来从指定的文件中读取一个字符串,并保存到字符数组中,它的原型为:
char *fgets ( char *str, int n, FILE *fp );
str 为字符数组,n 为要读取的字符数目,fp 为文件指针。
返回值:读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL;如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL。
注意: 读取到的字符串会在末尾自动添加 ‘\0’,n 个字符也包括 ‘\0’。也就是说,实际只读取到了 n-1 个字符,如果希望读取 100 个字符,n 的值应该为 101。例如:
#define N 101
char str[N];
FILE *fp = fopen("demo.txt", "r");
fgets(str, N, fp);
表示从 demo.txt 中读取100个字符,并保存到字符数组str中。
需要重点说明的是,在读取到 n-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管n的值多大,fgets() 最多只能读取一行数据,不能跨行。在C语言中,没有按行读取文件的函数,我们可以借助 fgets(),将n的值设置地足够大,每次就可以读取到一行数据。
fgets() 遇到换行时,会将换行符一并读取到当前字符串。该示例的输出结果之所以和 demo.txt 保持一致,该换行的地方换行,就是因为 fgets() 能够读取到换行符。而 gets() 不一样,它会忽略换行符。
【示例】一行一行地读取文件
#include <stdio.h>
#include <stdlib.h>
#define N 100
int main(){
FILE *fp;
char str[N+1];
if( (fp=fopen("demo.txt","rt")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
while(fgets(str, N, fp) != NULL){
printf("%s", str);
}
fclose(fp);
system("pause");
return 0;
}
1.4.2.2. 写字符串函数fputs
fputs() 函数用来向指定的文件写入一个字符串,它的原型为:
int fputs( char *str, FILE *fp );
str 为要写入的字符串,fp 为文件指针。写入成功返回非负数,失败返回EOF。例如:
char *str = "http://c.biancheng.net";
FILE *fp = fopen("demo.txt", "at+");
fputs(str, fp);
表示把把字符串 str 写入到 demo.txt 文件中。
【示例】向上例中建立的 demo.txt 文件中追加一个字符串。
#include<stdio.h>
int main(){
FILE *fp;
char str[102] = {0}, strTemp[100];
if( (fp=fopen("demo.txt", "at+")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
printf("Input a string:");
gets(strTemp);
strcat(str, "\n");
strcat(str, strTemp);
fputs(str, fp);
fclose(fp);
return 0;
}
运行程序,输入C C++ Java Linux Shell,打开 demo.txt,文件内容为:
C语言中文网
http://c.biancheng.net
一个学习编程的好网站!
C C++ Java Linux Shell
1.4.3. 以数据块形式读写文件
fgets() 有局限性,每次最多只能从文件中读取一行内容,因为 fgets 遇到换行符就结束读取。如果希望读取多行内容,需要使用 fread 函数;相应地写入函数为 fwrite。
1.4.3.1. fread() 函数
fread() 函数用来从指定文件中读取块数据。所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制。fread() 的原型为:
size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );
1.4.3.2. fwrite() 函数
fwrite() 函数用来向文件中写入块数据,它的原型为:
size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );
对参数的说明:
(1) ptr 为内存区块的指针,它可以是数组、变量、结构体等。fread() 中的 ptr 用来存放读取到的数据,fwrite() 中的 ptr 用来存放要写入的数据。
(2) size:表示每个数据块的字节数。
(3) count:表示要读写的数据块的块数。
(4) fp:表示文件指针。
理论上,每次读写 size*count 个字节的数据。
size_t 是在 stddef.h 头文件中使用 typedef 定义的数据类型,表示无符号整数,也即非负数,常用来表示数量。
返回值:返回成功读写的块数,也即 count。
如果返回值小于 count:
对于 fwrite() 来说,肯定发生了写入错误,可以用 ferror() 函数检测。
对于 fread() 来说,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
【示例】从键盘输入一个数组,将数组写入文件再读取出来。
#include<stdio.h>
#define N 5
int main(){
//从键盘输入的数据放入a,从文件读取的数据放入b
int a[N], b[N];
int i, size = sizeof(int);
FILE *fp;
if( (fp=fopen("demo2.txt", "rb+")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
//从键盘输入数据 并保存到数组a
for(i=0; i<N; i++){
scanf("%d", &a[i]);
}
//将数组a的内容写入到文件
fwrite(a, size, N, fp);
//将文件中的位置指针重新定位到文件开头
rewind(fp);
//从文件读取内容并保存到数组b
fread(b, size, N, fp);
//在屏幕上显示数组b的内容
for(i=0; i<N; i++){
printf("%d ", b[i]);
}
printf("\n");
fclose(fp);
return 0;
}
运行结果:
23 409 500 100 222↙
23 409 500 100 222
fwrite()/fread() 函数直接操作字节,建议使用二进制方式打开文件。
打开 demo.txt,发现文件内容根本无法阅读。这是因为我们使用"rb+"方式打开文件,数据以二进制形式写入文件,一般无法阅读。
数据写入完毕后,位置指针在文件的末尾,要想读取数据,必须将文件指针移动到文件开头,这就是rewind(fp);的作用。更多关于rewind函数的内容.
文件的后缀不一定是 .txt,它可以是任意的,你可以自己命名,例如 demo.ddd、demo.doc、demo.diy 等。
【示例】从键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据显示在屏幕上。
#include<stdio.h>
#define N 2
struct stu{
char name[10]; //姓名
int num; //学号
int age; //年龄
float score; //成绩
}boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa = boya;
pb = boyb;
if( (fp=fopen("demo.txt", "wb+")) == NULL ){
printf("Cannot open file, press any key to exit!\n");
getch();
exit(1);
}
//从键盘输入数据
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
}
//将数组 boya 的数据写入文件
fwrite(boya, sizeof(struct stu), N, fp);
//将文件指针重置到文件开头
rewind(fp);
//从文件读取数据并保存到数据 boyb
fread(boyb, sizeof(struct stu), N, fp);
//输出数组 boyb 中的数据
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
运行结果:
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom 2 15 90.500000
Hua 1 14 99.000000
1.5. 文件的输入函数fscanf和输出函数fprintf的应用
fscanf() 和 fprintf() 函数与前面使用的 scanf() 和 printf() 功能相似,都是格式化读写函数,两者的区别在于 fscanf() 和 fprintf() 的读写对象不是键盘和显示器,而是磁盘文件。
这两个函数的原型为:
int fscanf ( FILE *fp, char * format, ... ); int fprintf ( FILE *fp, char * format, ... );
fp 为文件指针,format 为格式控制字符串,… 表示参数列表。与 scanf() 和 printf() 相比,它们仅仅多了一个 fp 参数。例如:
FILE *fp;
int i, j;
char *str, ch;
fscanf(fp, "%d %s", &i, str);
fprintf(fp,"%d %c", j, ch);
fprintf() 返回成功写入的字符的个数,失败则返回负数。fscanf() 返回参数列表中被成功赋值的参数个数。
【示例】用 fscanf 和 fprintf 函数来完成对学生信息的读写。
#include<stdio.h>
#define N 2
struct stu{
char name[10];
int num;
int age;
float score;
} boya[N], boyb[N], *pa, *pb;
int main(){
FILE *fp;
int i;
pa=boya;
pb=boyb;
if( (fp=fopen("demo.txt","wt+")) == NULL ){
printf("Cannot open file, press any key exit!");
getch();
exit(1);
}
//从键盘读入数据,保存到boya
printf("Input data:\n");
for(i=0; i<N; i++,pa++){
scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
}
pa = boya;
//将boya中的数据写入到文件
for(i=0; i<N; i++,pa++){
fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);
}
//重置文件指针
rewind(fp);
//从文件中读取数据,保存到boyb
for(i=0; i<N; i++,pb++){
fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
}
pb=boyb;
//将boyb中的数据输出到显示器
for(i=0; i<N; i++,pb++){
printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
}
fclose(fp);
return 0;
}
运行结果:
Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom 2 15 90.500000
Hua 1 14 99.000000
打开 demo.txt,发现文件的内容是可以阅读的,格式非常清晰。用 fprintf() 和 fscanf() 函数读写配置文件、日志文件会非常方便,不但程序能够识别,用户也可以看懂,可以手动修改。
如果将 fp 设置为 stdin,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同;设置为 stdout,那么 fprintf() 函数将会向显示器输出内容,与 printf 的作用相同。例如:
#include<stdio.h>
int main(){
int a, b, sum;
fprintf(stdout, "Input two numbers: ");
fscanf(stdin, "%d %d", &a, &b);
sum = a + b;
fprintf(stdout, "sum=%d\n", sum);
return 0;
}
运行结果:
Input two numbers: 10 20↙
sum=30
1.6. 文件的定位,包括rewind函数和fseek函数以及ftell函数的应用
1.6.1. 文件随机读写
实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。
移动文件内部位置指针的函数主要有两个,即 rewind() 和 fseek()。
1.6.1.1. 文件定位函数rewind()
rewind() 用来将位置指针移动到文件开头,前面已经多次使用过,它的原型为:
void rewind ( FILE *fp );
1.6.1.2. 文件定位函数fseek()
fseek()用来将位置指针移动到任意位置,它的原型为:
int fseek ( FILE *fp, long offset, int origin );
参数说明:
- fp 为文件指针,也就是被移动的文件。
- offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。
- origin 为起始位置,也就是从何处开始计算偏移量。C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾,每个位置都用对应的常量来表示:
起始点 | 常量名 | 常量值 |
---|---|---|
文件开头 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
1.6.1.3. 获取文件当前访问位置函数ftell()
ftell()用来返回当前文件访问位置,它的原型为:
long ftell( FILE * fp );
如果发生错误ftell( )将返回-1.
下面是一个小demo,三个函数都有涉及:
#include<stdio.h>
#define MAX_LINE 256
int main(int argc,char *argv[]){
FILE *fp;
long lOffset = 0L;
char sLine[MAX_LINE] = "";
char *result = NULL;
int lineno = 0;
/* ... */
if ((fp = fopen(argv[2], "r")) == NULL){
fprintf(stderr, "Unable to open file %s\n", argv[2]);
exit( -1 );
}
do{
lOffset = ftell( fp ); // Bookmark the beginning of
// the line we're about to read.
if ( -1L == lOffset )
fprintf( stderr, "Unable to obtain offset in %s\n", argv[2] );
else
lineno++;
if (!fgets( sLine, MAX_LINE, fp ) ) // Read next line from file.
{
fprintf( stderr, "Unable to read from %s\n", argv[2] );
break;
}
} while ( strstr( sLine, argv[1] ) == NULL ); // Test for argument in sLine.
/* Dropped out of loop: Found search keyword or EOF */
if ( feof( fp ))
{
fprintf( stderr, "Unable to find \"%s\" in %s\n", argv[1], argv[2] );
rewind( fp );
}
else
{
printf( "%s (%d): %s\n", argv[2], lineno, sLine );
fseek( fp, lOffset, 0 ); // Set file pointer at beginning of
// the line containing the keyword
}
return 0;
}
本文未能对C语言的文件I/O操作的每个函数详细解读,请谅解.
参考资料:
http://c.biancheng.net/cpp/u/c13/
C: In a Nutshell(c语言核心技术)