c语言文件输入输出函数小结

       一个文件就是磁盘上一段命名的存储区。对于linux系统,所有的一切都是文件。一直以来对文件的读写操作总不太熟悉,小结一下。

      一般编写c程序时,系统自动为您打开三个文件。这三个文件分别是标准输入,标准输出和标准错误输出,对应的文件描述符分别为0,1,2.系统默认标准输入设备为键盘,标准输出设备为显示器。

      写一个常用的计数程序先练练手。

#include<stdio.h>

#include <stdlib.h>
int main(int argc,char *argv[])
{
	int ch;            //为什么不用char ch?
	int count=0;
    FILE *file=NULL;
	if((file=fopen(argv[1],"r"))==NULL)
	{
        printf("open file error\n");
		exit(1);
	}
    while((ch=getc(file))!=EOF)
	{
        putc(ch,stdout);
		count++;
	}
	fclose(file);
	printf("file %s contain %d characteres\n",argv[1],count);
	return 0;
}

    标准IO  

      首先介绍fopen()函数。

      FILE *fopen(char * path, const char *mode)

      其中mode为模式字符串,可以有以下几种形式:

       

模式字符串意义
“r"  以只读的方式打开一个文件
“w"打开一个文件,可以写入,如果文件不存在,就创建,存在,则先将文件清0再写入
”a"打开一个文本文件,向已有文件尾部追加内容,如果文件不存在,先创建
“r+"打开文本文件,可以读取也可以写入
”w+"打开文本文件,可以读取和写入,文件存在先将长度清0,不存在先创建
“a+"打开文本文件,可以读取和写入,向文件末尾追加,文件不存在先创建

      getc()

      int getc(FILE *file)

     从文件file中读取一个字符。那么要如何知道文件已经到达文件的结尾了呢,在文件末尾,getc函数会返回一个特殊值EOF,注意,这是针对文本文件而言的。因为我们知道文本文件字符以ASCII码表示,可以是0-127,故不可能为EOF(END OF FILE 是一个宏定义,具体值是-1),,所以可以用于判断文本文件是否结束。

     那么如果对于一个二进制文件,我也要判断文件是否读到结尾?其实我之前写的那个程序就可以,因为对于二进制文件,主要是怕字符FF分不清是文件结束符标志还是真的二进制字符,但是如果我声明的是int ch,此时判断文件标示符的EOF就成了0XFFFFFFFF,它就不可能和0x000000FF相等了,这样也就可以了。

     但其实c语言还提供了另外一种判断文件是否结束的标志feof()。

     int feof(FILE *file)

     遇到文件结束,返回非0,否则返回0.

     一般常用方式如下:

while(!feof(file))
	{
        ch=getc(file);
		printf("%c",ch);
	}


        ch=getc(file);
	while(!feof(file))
	{
            printf("%c",ch);
            ch=getc(file);
	}

      两者没有显著区别,但是上一种 确是错误的。会输出一个乱码字符。原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。这样就多输出了一个-1(即FF)。

       但是我们平时可以完全用EOF来代替feof吗?理论上是不可以的,因为getc在出错返回时返回的也是-1,所以尽量还是用feof吧。

     文件IO

      前面主要针对标准输入 输出IO,下面介绍与之类似的几个文件IO函数。主要区别在于他们需要制定一个文件描述符。主要有四个函数,分别为fprintf(),fscanf(),fgets(),fputs()。
      这几个函数我在字符串小结一节也说了。这里把它复制过来。
      

 字符串输入:

     c库提供三个读取字符串函数,为scanf(),gets(),fgets();
     gets从系统的标准输入设备(通常是键盘)获得一个字符串,字符串没有预定的长度,所以gets需要知道何时结束。在读取字符串时遇到换行符,它读取换行符之前(不包括换行符)的所有字符。在这些字符后加一个空字符(\0),把字符传交给调用它的程序。再读取换行符并丢弃如果出错和到达文件末尾,返回NULL
     函数原型 char *gets(char *s),将读取到的字符串存在s中。
     char *fgets(char *buf, int bufsize, FILE *stream)
     bufsiz说明最大的读入字符数,最多读取bufsiz-1个字符到buf中,或者遇到换行符为止。stream为流,从键盘输入可以用stdin。读取到的字符串存储在buf中,注意,与gets不同,它将读到的换行符不丢弃。函数调用成功返回buf,失败或者读到文件结尾返回NULL。故也不能用NULL表示是否出错。
     int fscanf(FILE *file,const char * format,[argument])
     format为格式字符串。它与fgets的差别在于它遇到空格和换行符结束。注意,它是遇到空格也结束,而fgets是遇到换行符才结束。返回值:成功返回读入参数的个数,其实就是1.失败返回-1.

   字符串输出:

     c库提供三个输出字符串的函数,为printf(),puts(),fputs()
     int puts(char *str) 
     将str字符串输出到stdout,它会自动在后面添加换行符,注意它只有遇到空字符才会停下来。
     int fputs(str,fp)
     从文件fp中读取字符串存入str中。它不会添加换行符。
     注意gets和puts配对,因为gets每次丢弃换行符,puts每次又加入换行符。fgets和fputs配对.
     int fprintf(FILE *file,const char* format,[argument])
     按格式输出字符串到file中,返回输出字符的个数,出错返回负数。
看个例程:
#include<stdio.h>
#include <stdlib.h>

#define MAX 30

int main(int argc,char *argv[])
{
    FILE *file=NULL;
	if((file=fopen(argv[1],"a+"))==NULL)
	{
        printf("open file error\n");
		exit(1);
	}
	printf("input some words\n");
    char word[MAX];
//	while(gets(word)!=NULL&&word[0]!='\0')      //用gets时,编译有警告提示,说是gets() is dangerous,please do not use it.查了
                                                //下,网上说gets不能保证输入的大小,如本例设置的word大小为MAX ,为30,结果未定义,用fgets
        while(fgets(word,MAX,stdin)!=NULL&&word[0]!='\n') //</span><span style="color:#ff0000;">fgets读取enter,将enter存入word,并加一个'\0',所以要用'\n'判断循环</span><span style="color:#333333;">       
	{                                                //</span><span style="color:#ff0000;">是否终止,而gets的话,会扔掉换行符,必须用'\0'判断结束。</span><span style="color:#333333;">
        fprintf(file,"%s",word);
	}
	puts("file content");
	rewind(file);           //回到文件开头
	while(fscanf(file,"%s",word)==1)           //每次读取一个word,到达空格就结束
		puts(word);
	if(fclose(file)!=0)
		fprintf(stderr,"error close file\n");
	return 0;
}
   

    小结:

    一般首先用fopen以各种方式打开文件,获取指向文件的指针。
    然后尽量使用fgets函数,因为它比gets函数安全。当然如果我们要控制每次读取的是一个字符串(到空格结束),我们可以使用fscanf,这个好像比c++ 的stringstream流好用,上次就一不小心重复使用流而未清空,使得结果出错,其它输出的话可以使用fprintf,可以按照自己定义的字符串格式进行输出。当然用fgets一次输出一行也行,
      以上提到的几个函数对于顺序文件读取足够用了,下面介绍几个从文件中随机存取的函数。
      int fseek(FILE *file,long offset,int origin)
      file 为打开的文件符。用fopen打开,offset为偏移量,origin有三种可能形式,分别为SEEK_SET,表示文件开头,SEEK_CUR,表示文件的当前位置,SEEK_END表示的是文件的末尾。
        如fseek(file,0,SEEK_CUR),表示当前位置。
      offset可正,可负,可为0.正,代表向前移(向前指的是向文件末尾),负,代表向后移,0代表不动。
      如果一切正常,fseek返回0,否则返回-1.
      long ftell(FILE *file)
      表示返回文件的当前位置。
      一个实例:
       
#include<stdio.h>

#define LEN 20

int main(int argc,char *argv[])
{
	char ch;
	FILE *file=NULL;
	file=fopen(argv[1],"rb");
	fseek(file,0L,SEEK_END);
	long count,last;
	last=ftell(file);
	for(count=1;count<=last;count++)
	{
                fseek(file,-count,SEEK_END);
		ch=getc(file);
		putchar(ch);
	}
	putchar('\n');
	fclose(file);
	return 0;
}

       对于前面所述的IO,例如fprintf,他们都是面向文本的,用于处理字符和字符串。尽管在计算机系统中,所有内容都是用二进制表示。但是逻辑上,文件还是分为文本文件和二进制文件,这是属于编解码方式的不同。对于二进制文件,内容在内存中的存放和在文件中的存放方式是一样的。可以理解为编码方式是一样的。然而,对于文本文件,也就是ASCII编码的文件,文件中存储和内存中是不一样的。比如说一个12345的int型变量,用fprintf写入文件中,它是按照一个个字符进行的编码,也就是分别按1,2,3,4,5的ASCII码来进行编码。但是对于二进制文件,可能直接将12345转化为二进制存放在文件中。有时如果我们想要用二进制的方式读取二进制文件,那么将用到fread和fwrite函数。
      size_t fwrite(const void *ptr,size_ t size,size_t num,FILE *file);
     将二进制数据写入文件。
     size_t fread(void *ptr,size_t size,size_t num,FILE* file) ;
     它们分别返回写入和读取到的真实的数目。
     常见实例代码是:
     
while((byte=fread(temp,sizeof(char),BUFSIZE,soource))>0)
{
    fwrite(temp,sizeof(char),bytes,dest);
}
     

    关键概念:

      c程序将输入看做字节流,流的来源可以是文件,键盘,或者是另外一个程序的输出。类似的,c也将输出看做字节流,流的目的可以是文件,也可以是屏幕。
      c如何解释输入输出流依赖使用的输入输出函数。如使用fread和fwrite,则使用二进制方式解释流状态。用fpritnf和gets之类的,则将字节流解释为字符。当我们要在不损失精度的前提下恢复数字数据,就使用二进制模式。如果想保存文本信息并且用普通文本编辑器可以查看的文件,则用getc,fpritnf()之类的 。


     下面给个读取文件的范例。
     
int  ch;      //用int来控制EOF
FILE *fp;
fp=fopen("test.txt","r");
while((ch=getc(fp))!=EOF)
{
     putchar(ch);
}

       

      

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值