C Primer Plus,十三

13.1 和文件进行通信

程序和文件进行通信的一种方式就是文件重定向。

13.1.1 文件是什么

一个文件通常就是磁盘上的一段命名的存储区。

C将文件看成是连续的字节序列,其中每一个字节都可以单独的读取。

ANSI C提供了文件的两种视图:文本视图和二进制视图。

13.1.2 文本视图和二进制视图

在二进制视图中,文件中的每个字节都可以为程序所访问。在文本视图中,程序看到的内容和文件的内容有可能不同。

例如使用文本视图读取文件时,将把行尾的本地环境表示法映射为C视图。与之类似,在输出的时候,也会将C视图中的行尾映射为本地环境表示法。

13.1.3 I/O级别

除了可以选择文件的视图,在大多数情况下,您可以在两个I/O级别中进行选择。

低级I/O使用操作系统提供的基本I/O服务,标准高级I/O使用一个标准的C库函数包和stdio.h头文件中的定义。

由于无法保证所有操作系统都可以用相同的低级I/O模型表示,所以ANSI C只支持标准I/O包。

13.1.4 标准文件

C程序自动为您打开三个文件。标准输入、标准输出、标准错误输出。

13.2 标准I/O

除了可移植性外,标准I/O包相对于低级I/O有两点优势。

第一、标准I/O包中包含很多专用的函数,可以方便的处理不同的I/O问题。

第二、对输入和输出进行了缓冲。这种缓冲大大提高了数据传输率。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main(int argc,char *argv[])
  4. {
  5. int ch,
  6. FILE *fp;
  7. long count=0;
  8. if(argc!=2)
  9. exit(1);
  10. if(fp=fopen(argv[1],"r")==NULL)
  11. exit(1);
  12. while((ch=fgetc(fp))!=EOF)
  13. {
  14. putc(ch,stdout);
  15. count++;
  16. }
  17. fclose(fp);
  18. return 0;
  19. }

13.2.1 检查命令行参数

首先,检查argc的值,产看是否有命令行参数。字符串argv[0]是该程序的名称。

exit()函数关闭所有打开的文件并终止程序。通常的约定是正常终止的程序传递值0,非正常终止的程序传递非0值。您也可以使用exit(EXIT_SUCCESS)和xit(EXIT_FAILURE)。

按照ANSI C,在最初调用的main()中使用return和调用exit()的效果相同。 //注意是“最初调用”,如果main()在一个递归程序中,exit()仍然会终止程序。但return将控制权移交给递归的前一级,直到最初。

13.2.2 fopen()函数

程序用fopen()打开文件,它的第一个参数是要打开的文件名,或者说是包含该文件名的字符串的地址。第二个参数用于指定文件打开模式的一个字符串。

如果使用任何一种“w”模式打开一个已有的文件,文件内容将被删除,以便程序以一个空文件开始操作。

程序成功的打开一个文件后,fopen()函数返回一个文件指针,其它I/O函数用这个指针来指定该文件。

如果不能打开文件,fopen()函数返回空指针。

13.2.3 getc()和putc()函数

这两个函数的工作方式和函数getchar()和putchar()非常相似,不同之处在于你需要告诉getc()和putc()函数它们要使用的文件。

例如ch=getc(fp),putc(ch,fpout);

putc(ch,stdout);第二个参数是标准输出相关的文件指针,所以其效果与putchar(ch)一样。

13.2.4 文件结尾

如果在尝试读入字符时发现已经达到文件结尾,getc()函数会返回一个特殊值EOF。

所以可以加一个条件while((ch=fgetc(fp))!=NULL)来测试。

13.2.5 fclose()函数

fclose(fp)函数关闭由指针fp指定的文件,同时根据需要刷新缓冲区。如果成功关闭,返回值0,否则返回EOF。

13.2.6 标准文件指针

stdin和键盘 stdout和输出 stderr和显示器,这些指针都是FILE指针类型。

13.3 文件I/O:fprintf()、fscanf()、fgets()和fputs()函数

13.3.1 fprintf()和fscanf()函数

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define MAX 40
  4. int main(void)
  5. {
  6. FILE *fp;
  7. char words[MAX];
  8. if((fp=fopen("words","a+"))==NULL)
  9. {
  10. fprintf(stdout,"Can't open.\n");
  11. exit(1);
  12. }
  13. while(gets(words)!=NULL&&words[0]!='\0')
  14. fprintf(fp,"%s",words);
  15. rewind(fp);
  16. while(fscanf(fp,"%s",words)==1)
  17. puts(words);
  18. if(fclose(fp)!=0)
  19. fprintf(stderr,"erroe\n");
  20. return 0;
  21. }

rewind()命令使程序回到文件开始处,这样最后的while循环就可以打印文件的内容。

13.3.2 fgets()和fputs()函数

fgets()函数接受三个参数,第一用于存储输入的地址。第二个参数为整数,表示输入字符串的最大长度。最后一个参数是文件指针,指向要读取的文件。

fgets(buf,MAX,fp) 该函数读取它所遇到的第一个换行字符的后面,或者读取比字符串的最大长度少一个的字符,或者读取到文件结尾。然后fgets()函数向末尾添加一个空字符以构成一个字符串。

所以字符串的最大长度代表字符的最大数目再加上一个空字符。如果在达到字符最大数目之前读完了一整行,它将在字符串的空字符之前添加一个换行符。

fputs()函数接受两个参数,它们一次是一个字符串的地址和一个文件指针,将字符串写入指定文件。fputs(but,fp);

13.3.3 gets()和fgets()函数

fgets()函数可以防止存储溢出,所以对于严格的编程来说,它是一个更好的选择。

13.4 随机存取:fseek()和ftell()函数

fseek()函数允许你像对待数组那样对待一个文件,在fopen()打开的文件直接移动到任意字节处。它接受3个参数,返回一个int值,ftell()函数以一个long类型值返回一个文件的当前位置。

在fseek()的三个参数中

第一个参数是一个指向被搜索文件的FILE指针。应该已经使用fopen()打开了该文件。

第二个参数称为偏移量,表示从起始点开始要移动的距离。这个参数必须是一个lon类型的值,可以为正(前移)、负(后移),也可以为零(保持不动)。

第三个参数是模式,用来标识起始点。SEEK_SET:文件开始 SEEK_CUR:文件当前 SEEK_END:文件结尾

例如:fseek(fp,0L,SEEK_SET);

如果一切正常,fseek()返回值为0。有错误则返回-1。

ftell()函数为long类型,它返回文件的当前位置。ftell()函数通过返回距文件开始处的字节数目来确定文件的位置,文件的第一个字节到文件开始处的距离是字节0,以此类推。

last=ftell(fp);把从文件开始到文件结尾的字节数目赋给last。

13.5 标准I/O内幕

通常使用标准I/O的第一步就是用fopen()打开一个文件。fopen()函数不仅打开一个文件,而且建立了一个缓冲区,还创建了一个包含文件和缓冲区相关数据的数据结构。

下一步就是调用stdio.h头文件中声明的某个输入函数,比如fscanf()等。会把一块数据从文件复制到缓冲区中。

输出函数将数据写入缓冲区,缓冲区满的时候,就将数据复制到文件中。

13.6 其它标准I/O函数

13.6.1 int ungetc(int c ,FILE *fp)函数

该函数将c指定的字符放回输入流中,如果向输入流中放人了一个字符,下一次调用标准输入函数就会读入那个字符。

例如 ch=getchar();ungetc(ch,stdin);

13.6.2 int fflush()函数

函数原型为int fflush(FILE *fp);它能将缓冲区任何未写的数据发送到一个由fp指定的输出文件中去。这个过程称为刷新缓冲区。如果fp是一个空指针,将刷新掉所有的输出缓冲。

13.6.3 int setvbuf()函数

函数原型是 int setvbuf(FILE * restrict fp,char * restrict buf,int mode ,size_t size);

setvbuf()函数建立了一个供标准I/O函数使用的替换缓冲区。打开文件后,在没有对流进行任何操作以前,可以调用这个函数。由fp来指定流,buf指向将使用的缓冲区。若buf为NULL,自动分配缓冲区。

size变量为setvbuf()函数指定数组的大小。

mode将从下列选项中选取:

_IOFBF表示完全缓冲,_IOLBF表示行缓冲,_IONBF表示无缓冲。如果成功执行,函数返回零值,否则返回一个非零值。

13.6.4 二进制I/O:fread()和fwrite()函数

如果把数据存储在一个使用与程序具有相同表示方法的文件中,就称数据以二进制形式存储。对于标准I/0,fread()和fwrite()函数提供了这种二进制服务。

实际上,所有的数据都是以二进制的方式进行存储的。如果文件中的全部数据都以字符编码的形式被解读,我们称该文件包含文本数据,如果部分或者全部以二进制形式解读,就称该文件包含二进制数据。

char buffer[256];fwrite(buffer,256,1,fp);第三个参数表示数据块数目,这一调用将一块256字节大小的数据块从缓冲区写入到文件。

double earning[10];fwrite(earnings,sizeof(double),10,fp);将数组中数据写入文件,数据分成10块,每块double大小。

fread(earnings,sizeof(double),10,fp);恢复前一个例子中的数组,返回成功读入的项目数。