前言:最近在看C语言的基本文件操作,遇到一个函数ungetc(),看了一些文章,发现并没有一篇真正写清楚的,都是转载来转再去,就那几句话说来说去,决定自己写一篇,来详细探讨这个函数的用法。
一、ungetc函数概述
我们都知道getc函数的作用,它的作用是从文件流中读取一个字符char,这个文件流可以是一般的文件流,也可以是标准文件流,顾名思义,ungetc的作用应该是和这个相反的,既然getc是“读出”一个char字符,那么ungetc当然就是将一个字符“放回”流了,意思很简单,下面来看一下一些常见的用法以及注意事项。
简而言之,ungetc的作用就是将一个字符放回输入流中。放回,放回,放回!!!重要的是说三遍。函数原型为:
int __cdecl ungetc(int Ch,FILE *_File);
1.1 标准输入流stdin不支持“放回”操作
ungetc ('c', stdin); //给标准输入流放回一个'c'字符
ungetc ('b', stdin);
ungetc ('a', stdin);
while(feof(stdin))
{
char c=getc(stdin);
putchar(c); //发现没有任何输出
}
但是很遗憾,上面的运行结果没有任何结果输出,按道理说,我依次给标准输入流中放进去 c b a 三个字符,应该再读出来才对啊,为什么不行呢?这是因为因为标准输入流stdin只支持从键盘输入数据哦!!!
结论一:
标准输入流stdin无法使用ungetc放回任何字符,标准输入stdin只支持从键盘输入字符;
1.2 普通文件输入流是否可以一次放回多个字符
看下面的代码:
FILE * ffile=fopen("file1.txt","r"); //打开一个文件输入流
ungetc ('c', ffile); //依次放回 c b a
ungetc ('b', ffile);
ungetc ('a', ffile);
char ch;
while(!feof(ffile))
{
ch=getc(ffile);
putchar(ch); //只能够输出一个字母c
}
按道理说,这里依次给文件输入流ffile放回了三个字符 c b a 啊,但是为什么只输出了一个c呢?
这个地方实际上只写入了一个,后面的b,a并没有写入,即每次只能写入一个,不能写入很多个。
即一个数据放回到缓冲区去后,没有将缓冲区的数据读出来 就再次试图把读出的数据放回到缓冲区去,数据是放不进去的 (可以把缓冲区看做一个可变化的容器,当你把试图用ungetc()函数把读出的数据放回到缓冲区,缓冲区这个容器就为这些数据分配相应的大小空间,之后这个空间是不变的,直到你把缓冲区的数据读出去,所以你在没有释放缓冲区时,再次想往缓冲区装数据是装不进去的。
结论二:
普通文件输入流可以使用ungetc放回一个字符,但是一次只能够放回一个,必须要等到放回的字符读出来之后再才能接着放回一个字符;
按照结论二,我们看下面的例子:
FILE * fffile=fopen("file2.txt","r");
int i=1;
char ch2;
while(!feof(fffile))
{
if(i==1)
ungetc ('c', fffile); //每一次只放回一个字符,然后又在下面取出来了
else if(i==2)
ungetc ('b', fffile);
else if(i==3)
ungetc ('a', fffile);
ch2=getc(fffile);
putchar(ch2); //依次输出cba
i++;
}
上面的运行结果为 cba ,
这是比较合理的,每一只放回了一个字符,放回c,取出c,然后接着放回b,取出b,再放回a,再取出a。验证了结论二。
1.3 综合案例
现在有一个文本文件,内容如下:
i love you too!
owhat?
ohow?
owhere?
我有下面的这一段代码:
FILE *fp;
int c;
char buffer [256];
fp = fopen("file.txt", "r");
if( fp == NULL )
{
perror("Error in opening file");
return(-1);
}
while(!feof(fp)) //判断是否是到达了文件结尾
{
c = getc (fp);
if( c == 'o' ) //如果读到了字符 'o' ,则像文件输入流中 “放回” 一个 '*' 字符
{
ungetc ('*', fp);
}
putchar(c);
}
/*运行结果为:
i lo*ve yo*u to*o*!
o*what?
o*ho*w?
o*where?
*/
为什么会是这样的结果呢?
分析可知,当读入一个字符遇到 'c' 之后,然后往标准输入流里面放回了一个 '*'字符,所以接下来会读取这个字符,然后再继续读取后面的内容。
总结:
注意ungetc函数的含义理解以及两个结论总结。