Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188789
  • 博文数量: 24
  • 博客积分: 261
  • 博客等级: 二等列兵
  • 技术积分: 306
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-29 08:25
文章存档

2015年(1)

2014年(1)

2013年(15)

2012年(7)

分类:

2012-11-26 10:16:38

原文地址:标准I/O相关函数 作者:草根老师

一、打开一个流



这三个函数的区别是:

(1)fopen打开路径名由pathname指示的一个文件
(2)freopen常用于一个打开的流重新定向。比如stdout是标准输出,我们可以把它重定向到由path指定的一个文件。
(3)fdopen取一个现存的文件描述符,并使一个标准的I/O流与该描述符相结合。



总结如下:


二、一步步探究

A.fopen函数

  1. #include <stdio.h>

  2. int main()
  3. {
  4.     FILE *fp;

  5.     if((fp = fopen("cyg_r.txt","r")) == NULL)
  6.     {
  7.         perror("fail to fopen");
  8.     }

  9.     printf("___________________________________\n\n");

  10.     if((fp = fopen("cyg_r .txt","r ")) == NULL)
  11.     {
  12.         perror("fail to fopen");
  13.     }

  14.     printf("___________________________________\n\n");

  15.     if((fp = fopen("cyg_w.txt","w")) == NULL)
  16.     {
  17.         perror("fail to fopen");
  18.     }

  19.     printf("___________________________________\n\n");

  20.     if((fp = fopen("cyg_w .txt","w ")) == NULL)
  21.     {
  22.         perror("fail to fopen");
  23.     }

  24.     printf("___________________________________\n\n");

  25.     if((fp = fopen("cyg_a.txt","a")) == NULL)
  26.     {
  27.         perror("fail to fopen");
  28.     }

  29.     printf("___________________________________\n\n");

  30.     if((fp = fopen("cyg_a .txt","a ")) == NULL)
  31.     {
  32.         perror("fail to fopen");
  33.     }
  34.     
  35.     return 0;
  36. }


从以上的探究发现,对于r和r 文件必须存在,否则出错,而对于 w,w 和a,a 如果文件不存则会新建。

B.freopen()函数




  1. #include <stdio.h>

  2. int main()
  3. {
  4.     FILE *fp;
  5.     char ch;
  6.     int i = 0;

  7.     if((fp = freopen("test.txt","w",stdout)) == NULL)
  8.     {
  9.         perror("fail to freopen");
  10.     }

  11.     printf("hello word\n");
  12.     
  13.     //恢复标准输出到终端
  14.     if((fp = freopen("/dev/tty","w",stdout)) == NULL)
  15.     {
  16.         perror("fail to freopen");
  17.     }

  18.     if((fp = freopen("test.txt","r",stdin)) == NULL)
  19.     {
  20.         perror("fail to freopen");
  21.     }
  22.     
  23.     do
  24.     {
  25.         ch = getchar();
  26.         putchar(ch);
  27.     }while(ch != '\n');

  28.     //恢复标准输入到键盘
  29.     if((fp = freopen("/dev/tty","r",stdin)) == NULL)
  30.     {
  31.         perror("fail to freopen");
  32.     }

  33.     ch = getchar();
  34.     
  35.     printf("ch = %c\n",ch);

  36.     return 0;
  37. }


三、关闭一个流



a.fclose()调用成功返回0,失败返回EOF,并设置errno

b.在该文件被关闭之前,刷新缓存中的数据。如果标准I/O库已经为该流自动分配了一个缓存,则释放此缓存。

c.当一个进程正常终止时(直接调用exit函数,或从main函数返回),则所有带未写缓存数据的标准I/O流都被刷新,所有打开的标准I/O流都被关闭

d.在调用fclose()关闭流后对流进行的任何操作,包括再次调用fclose(),其结果都将是未知的。

四、读和写流

一旦打开了流,则可在三种不同类型的非格式化I/O中进行选择,对其进行读、写操作 。

(1)每次一个字符的I/O。使用fgetc()和fputc()一次读或写一个字符,如果流逝带缓存的,则标准I/O函数处理所有缓存。 




A.这三个函数的返回:若成功则为读取的一个字符,若已处文件尾端或出错则为EOF

B.函数getchar()等同于getc(stdin)

注意:不管是出错还是到达文件尾端,这三个函数都返回同样的值。为了区分这两种不同的情况,必须调用ferror()或feof()。

C.getc()的实现是一个宏,而fgetc()是一个函数

从上面观察我们发现,这几个函数都是用来读取一个字符的,但是返回值都是int,为什么要这样做呢?我们看下面的探究

<1>字节的读取

        在正常情况下,fgetc或getc以unsigned char的方式读取文件流,扩张为一个整数,并返回。也就是说,fgetc或getc从文件流中取一个字节,并加上24个0,称为一个小于256的整数,然后返回。

int c;

while((c = fgetc(rfp)) != -1)//-1就是EOF
fputc(c,wfp);

上面fputc中的c虽然是整数,但在fputc将其写入文件流之前,又把整数的高24位去掉了,因此fgetc,fputc配合能够实现文件复制。 

<2>判断文件结束
        
        多数人认为文件中有一个EOF,用于表示文件的结尾。但这个观点实际上是错误的,在文件所包含的数据中,并没有什么文件结束符。对fgetc或getc而言,如果不能从文件中读取,则返回一个整数-1,这就是所谓的EOF,返回 EOF无非是出现了两种情况,一是文件已经读完;二是文件读取出错,反正是读不下去了。

注意:在正常读取的情况下,返回的整数均小于256,,即0x00-0xff,而读不出返回的是0xffffffff,但,假如你用fputc把0xffffffff向文件里头写,高24位被屏蔽,写入的将是0xff

<3>0xff会使我们混淆吗?

    不会,前提是,接收返回值的c要按原型定义为int。    
    如果下一个读取的字符将为oxff,则

    int c;
    c = fgetc(rfp);//c = 0x000000ff
    if(c != -1)//当然不等,-1是0xffffffff
    fputc(c,wfp);//oxff写入成功

    注意:字符0xff,其本身并不是EOF

<4>将c定义为char

    假定下一个读取的字符为0xff则
    char c;
    c = fgetc(rfp);//fgetc(rfp)的值为0x000000ff,暗中降为字节,c =0xff
    if(c != -1)//字符与整数比较?c被带符号(signed)扩展为0xffffffff,条件成立,文件复制提前结束
    fputc(c,wfp);

<5>将c定义为unsigned char
    
    unsigned char c;
    c =fgetc(rfp);
    if(c != -1)//此时条件将恒成立,因为c是一个无符号数,不可能是-1的。
    fputc(c,wfp);

<6>为何需要feof?

fgetc返回-1时,我们任然无法确信文件已经结束,因为可能是读取错误!这时我们需要feof和ferror。



FILE *fp,fp指向一个很复杂的数据结构,feof是通过这个结构中的标志来判断文件是否结束的。如果文件用fgetc读取,刚好把最后一个字符读出时,fp中的EOF标志不会打开,这时用feof判断,将会得到文件尚未结束的结论。






A.putchar(c)等价于putc(c,stdout)
B.出错返回EOF
C.getc()/getchar()/putc()/putchar()实现为宏,fgetc()/fputc()实现为函数

案例:

  1. #include <stdio.h>

  2. FILE *open_file(const char *name,const char *mode)
  3. {
  4.     FILE *fp;
  5.     
  6.     if((fp = fopen(name,mode)) == NULL)
  7.     {
  8.         perror("Fail to fopen");
  9.         return NULL;
  10.     }

  11.     return fp;
  12. }

  13. int do_copy(FILE *src_fp,FILE *dest_fp)
  14. {
  15.     int ch;
  16.     
  17.     while((ch = fgetc(src_fp)) != EOF)
  18.     {
  19.         fputc(ch,dest_fp);
  20.     }
  21.     
  22.     if(feof(src_fp))
  23.     {
  24.         printf("We have read end of file\n");
  25.         return 0;
  26.     }else{
  27.         printf("Read file error!\n");
  28.         return -1;
  29.     }
  30. }

  31. int main(int argc,char *argv[])
  32. {
  33.     FILE *rfp,*wfp;

  34.     if(argc < 2)
  35.     {
  36.         fprintf(stderr,"usage : %s src_file dest_file\n",argv[0]);
  37.         return -1;
  38.     }
  39.     
  40.     if((rfp = open_file(argv[1],"r")) == NULL)
  41.     {
  42.         return -1;
  43.     }

  44.     if((wfp = open_file(argv[2],"w")) == NULL)
  45.     {
  46.         return -1;
  47.     }
  48.     
  49.     if(do_copy(rfp,wfp) < 0)
  50.     {
  51.         return -1;
  52.     }

  53.     return 0;
  54. }

(2)每次一行的I/O。使用fgets和fputs一次读或写

A.函数原型:



B.函数解释



C.函数返回值



总结:

fgets在三种情况下,会结束读取

<1>成功读取了size-1个字符
<2>读取的时候遇到'\n'
<3>读到文件尾

这三种情况下,fgets都必在一件事情,就是在结束的时候,都会加上'\0'

fgets()返回值为NULL时,可能流中没有字符可读了,可能读取出错

A.函数原型



B.函数解释



C.函数返回值




总结:

fputs函数是将一个字符串写入流,但是不会将'\0'进行写入

案例探究:


  1. #include <stdio.h>
  2. #include <string.h>

  3. int main()
  4. {
  5.     char buf[15] = "aaa\0bbb\ncccccc";
  6.     FILE *fp;
  7.     int i = 0;
  8.     char testbuf[20];

  9.     if((fp = fopen("test.txt","w+")) == NULL)
  10.     {
  11.         perror("fail to fopen");
  12.         return -1;
  13.     }
  14.     
  15.     for(i = 0;i <sizeof(buf);i ++)
  16.         fputc(buf[i],fp);
  17.     
  18.     memset(testbuf,'b',sizeof(testbuf));
  19.     
  20.     fseek(fp,0,SEEK_SET);
  21.     
  22.     system("od -c test.txt");

  23.     if(fgets(testbuf,sizeof(testbuf),fp) == NULL)
  24.     {
  25.         perror("fail to testbuf");
  26.     }
  27.     
  28.     putchar('\n');

  29.     for(i = 0;i < sizeof(testbuf);i ++)
  30.     {
  31.         printf("%3d",testbuf[i]);
  32.     }

  33.     printf("\n\n");

  34.     fputs(testbuf,fp);

  35.     fflush(fp);
  36.     
  37.     system("od -c test.txt");

  38.     return 0;
  39. }


从上面我们可以看到,fgets函数在读取文件的时候,遇到'\n'结束,并在最后面加上了'\0';

fputs函数对testbuf数组中内容进行输入时,只输入了'\0'之前的字符

gets()与puts()

A.函数原型:char *gets(char *buffer);

gets()函数是一个不推荐使用的函数。功能:从stdin流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在 buffer指针所指向的字符数组中。换行符不作为读取串的内容,读取的换行符被转换为NULL值,并由此来结束字符串。

注意:
<1>本函数可以无限读取。不会判断上限,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。
<2>gets(s)与scanf("%s",s)相似,但完全不同,使用scanf("%s",s)函数输入字符串时存在一个问题,就是如果输入了空格会认为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行符为止。

B.函数原型:int   puts(const char *s)

函数解释:



此函数比gets安全,它的功能是把s指向的字符串输出到stdout,并在最后加上换行符。

(3)直接I/O。fread和fwrite函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中读或写一个结构。

A.函数原型



B.函数解释



C.函数返回值




  1. #include <stdio.h>

  2. #define MAX 50

  3. FILE *open_file(const char *name,const char *mode)
  4. {
  5.     FILE *fp;
  6.     
  7.     if((fp = fopen(name,mode)) == NULL)
  8.     {
  9.         perror("Fail to fopen");
  10.         return NULL;
  11.     }

  12.     return fp;
  13. }

  14. int do_copy(FILE *src_fp,FILE *dest_fp)
  15. {
  16.     char buf[MAX];
  17.     int n;
  18.     
  19.     while( (n = fread(buf,sizeof(char),sizeof(buf),src_fp)) == sizeof(buf))
  20.     {
  21.         fwrite(buf,1,n,dest_fp);
  22.     }
  23.     
  24.     if(feof(src_fp))
  25.     {

  26.         fwrite(buf,sizeof(char),n,dest_fp);
  27.         printf("Read end of file\n");
  28.     
  29.     }else{
  30.         
  31.         printf("Read error!\n");
  32.         return -1;
  33.     }

  34.     return 0;
  35. }

  36. int main(int argc,char *argv[])
  37. {
  38.     FILE *rfp,*wfp;

  39.     if(argc < 2)
  40.     {
  41.         fprintf(stderr,"usage : %s src_file dest_file\n",argv[0]);
  42.         return -1;
  43.     }
  44.     
  45.     if((rfp = open_file(argv[1],"r")) == NULL)
  46.     {
  47.         return -1;
  48.     }

  49.     if((wfp = open_file(argv[2],"w")) == NULL)
  50.     {
  51.         return -1;
  52.     }
  53.     
  54.     if(do_copy(rfp,wfp) < 0)
  55.     {
  56.         return -1;
  57.     }

  58.     return 0;
  59. }
五、定位流



fseek函数是用来定位流的文件指示器的位置的,其中whence有三个参数

SEEK_SET从文件头开始
SEEK_CUR从文件指示器当前位置开始
SEEK_END从文件尾开始

如:

fseek(fp,0,SEEK_SET);将文件指示器定位到文件头
fseek(fp,35,SEEK_CUR);将文件指示器从当前位置向后移动35byte;
fseek(fp,0,SEEK_END);将文件指示器定位到文件尾部

ftell函数用来获得文件指示器当前位置
rewind函数用来将文件指示器重新定位到文件头

fgetpos和fsetpos函数相当于ftell和fseek(whence设置为SEEK_SET)的另一种写法,读取文件当前位置并保存在pos中,或用pos指向的值设置当前文件位置。

案例:将一个文件的前一半放到file1中,后一半放到file2中

  1. #include <stdio.h>
  2. #include <string.h>

  3. #define MAX 10

  4. int main(int argc,char *argv[])
  5. {
  6.     FILE *rfp,*wfp1,*wfp2;
  7.     int file_size = 0,count = 0,nsize = 0;
  8.     char buf[MAX];

  9.     if(argc < 4)
  10.     {
  11.         fprintf(stderr,"usage : %s file\n",argv[0]);
  12.         return -1;
  13.     }

  14.     if((wfp1 = fopen(argv[2],"w+")) == NULL)
  15.     {
  16.         perror("fail to fopen");
  17.         return -1;
  18.     }

  19.     if((wfp2 = fopen(argv[3],"w")) == NULL)
  20.     {
  21.         perror("fail to fopen");
  22.         return -1;
  23.     }
  24.     
  25.     if((rfp = fopen(argv[1],"r")) == NULL)
  26.     {
  27.         perror("fail to fopen");
  28.         return -1;
  29.     }

  30.     fseek(rfp,0,SEEK_END);
  31.     
  32.     file_size = ftell(rfp);

  33.     printf("file_size = %d\n",file_size);

  34.     //fseek(rfp,0,SEEK_SET);
  35.     rewind(rfp);

  36. /*
  37.     while(fgets(buf,sizeof(buf),rfp) != NULL)
  38.     {
  39.         count += strlen(buf);
  40.         if(count <= file_size/2)
  41.             fputs(buf,wfp1);
  42.         else
  43.             fputs(buf,wfp2);
  44.     }
  45. */
  46.     int ch;

  47.     while((ch = fgetc(rfp)) != EOF)
  48.     {
  49.         count ++;
  50.         if(count <= file_size/2)
  51.             fputc(ch,wfp1);
  52.         else
  53.             fputc(ch,wfp2);
  54.     }

  55.     if(!feof(rfp))
  56.     {
  57.         printf("Read %s file error!\n",argv[1]);
  58.         return -1;
  59.     }

  60.     return 0;
  61. }
编译运行:



 五、格式化I/O

A.格式化输出



printf函数将格式化数据写到标准输出,fprintf写至指定的流,sprintf将格式化的字符输入数组buf中 。fprintf在该数组的尾端自动加一个NULL字节,但该字节不包括在返回值中。



运行结果:



B.格式化输入



阅读(1054) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~