C语言文件I/O操作值ungetc()函数深入详解

前言:最近在看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函数的含义理解以及两个结论总结。

  • 36
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
所有的 C / C++ 函数 Constructors (cppstring) Constructors (cppvector) Operators (cppbitset) Operators (cppdeque) Operators (cppstack) Operators (cppstring) Operators (cppvector) abort (stdother) abs (stdmath) acos (stdmath) any (cppbitset) append (cppstring) asctime (stddate) asin (stdmath) assert (stdother) assign (cppdeque) assign (cpplist) assign (cppstring) assign (cppvector) at (cppdeque) at (cppstring) at (cppvector) atan (stdmath) atan2 (stdmath) atexit (stdother) atof (stdstring) atoi (stdstring) atol (stdstring) back (cppdeque) back (cpplist) back (cppqueue) back (cppvector) bad (cppio) begin (cppdeque) begin (cpplist) begin (cppmap) begin (cppmultimap) begin (cppmultiset) begin (cppset) begin (cppstring) begin (cppvector) bsearch (stdother) c_str (cppstring) calloc (stdmem) capacity (cppstring) capacity (cppvector) ceil (stdmath) clear (cppdeque) clear (cppio) clear (cpplist) clear (cppmap) clear (cppmultimap) clear (cppmultiset) clear (cppset) clear (cppvector) clearerr (stdio) clock (stddate) compare (cppstring) copy (cppstring) cos (stdmath) cosh (stdmath) count (cppbitset) count (cppmap) count (cppmultimap) count (cppmultiset) count (cppset) ctime (stddate) data (cppstring) #define (preproc) difftime (stddate) div (stdmath) empty (cppdeque) empty (cpplist) empty (cppmap) empty (cppmultimap) empty (cppmultiset) empty (cpppriorityqueue) empty (cppqueue) empty (cppset) empty (cppstack) empty (cppstring) empty (cppvector) end (cppdeque) end (cpplist) end (cppmap) end (cppmultimap) end (cppmultiset) end (cppset) end (cppstring) end (cppvector) eof (cppio) equal_range (cppmap) equal_range (cppmultimap) equal_range (cppmultiset) equal_range (cppset) erase (cppdeque) erase (cpplist) erase (cppmap) erase (cppmultimap) erase (cppmultiset) erase (cppset) erase (cppstring) erase (cppvector) #error (preproc) exit (stdother) exp (stdmath) fabs (stdmath) fail (cppio)
目 录 译者序 序 第1版序 前言 第1章 基本概念 1.1 入门 1.2 变量与算术表达式 1.3 for语句 1.4 符号常量 1.5 字符输入输出 1.5.1 文件复制 1.5.2 字符计数 1.5.3 行计数 1.5.4 单词计数 1.6 数组 1.7 函数 1.8 变元—按调用 1.9 字符数组 1.10 外部变量与作用域 第2章 类型、运算符与表达式 2.1 变量名 2.2 数据类型与大小 2.3 常量 2.4 说明 2.5 算术运算符 2.6 关系运算符与逻辑运算符 2.7 类型转换 2.8 加一与减一运算符 2.9 按位运算符 2.10 赋运算符与赋表达式 2.11 条件表达式 2.12 运算符优先级与表达式求次序 第3章 控制流 3.1 语句与分程序 3.2 if-else语句 3.3 else-if语句 3.4 switch语句 3.5 while与for循环语句 3.6 do-while循环语句 3.7 break语句与continue语句 3.8 goto语句与标号 第4章 函数与程序结构 4.1 函数的基本知识 4.2 返回非整数函数 4.3 外部变量 4.4 作用域规则 4.5 头文件 4.6 静态变量 4.7 寄存器变量 4.8 分程序结构 4.9 初始化 4.10 递归 4.11 C预处理程序 4.11.1 文件包含 4.11.2 宏替换 4.11.3 条件包含 第5章 指针与数组 5.1 指针与地址 5.2 指针与函数变元 5.3 指针与数组 5.4 地址算术运算 5.5 字符指针与函数 5.6 指针数组与指向指针的指针 5.7 多维数组 5.8 指针数组的初始化 5.9 指针与多维数组 5.10 命令行变元 5.11 指向函数的指针 5.12 复杂说明 第6章 结构 6.1 结构的基本知识 6.2 结构与函数 6.3 结构数组 6.4 结构指针 6.5 自引用结构 6.6 查找表 6.7 类型定义 6.8 联合 6.9 位字段 第7章 输入与输出 7.1 标准输入输出 7.2 格式输出—printf函数 7.3 变长变元表 7.4 格式输入—scanf函数 7.5 文件访问 7.6 错误处理—stderr和exit函数 7.7 行输入输出 7.8 其他函数 7.8.1 字符串处理函数 7.8.2 字符类测试和转换函数 7.8.3 ungetc函数 7.8.4 命令执行函数 7.8.5 存储管理函数 7.8.6 数学函数 7.8.7 随机数发生器函数 第8章 UNIX系统界面 8.1 文件描述符 8.2 低级I/O—read和write系统调用 8.3 open、creat、close和unlink系统调用 8.4 随机访问—lseek系统调用 8.5 实例—fopen和getc函数的一种实现 方法 8.6 实例—目录显示 8.7 实例—存储分配程序 附录A 参考手册 A.1 引言 A.2 词法规则 A.3 语法符号 A.4 标识符的含义 A.5 对象和左 A.6 转换 A.7 表达式 A.8 说明 A.9 语句 A.10 外部说明 A.11 作用域与连接 A.12 预处理 A.13 语法 附录B 标准库 B.1 输入与输出:<stdio.h> B.2 字符类测试:<ctype.h> B.3 字符串函数:<string.h> B.4 数学函数:<math.h> B.5 实用函数:<stdlib.h> B.6 诊断:<assert.h> B.7 变量变元表:<stdarg.h> B.8 非局部跳转:<setjmp.h> B.9 信号处理:<signal.h> B.10 日期与时间函数:<time.h> B.11 由实现定义的限制:<limits.h>和 <float.h> 附录C 变更小结

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值