大家好,欢迎来到IT知识分享网。
基础定义与陷阱
int ungetc(int c, FILE *stream);
- 核心功能:将字符c回退到输入流stream,后续读取顺序为后进先出
- 隐藏规则:
- 仅保证至少1次成功回退(C99标准)
- 多次回退后读取顺序:
- ungetc(‘A’, fp); ungetc(‘B’, fp); → 读取顺序为B→A
- 文件结束符EOF处理:调用ungetc后清除流的EOF状态
实战场景
场景1:动态类型解析器
void parse_value(FILE *fp) { int c = fgetc(fp); if (isdigit(c)) { ungetc(c, fp); // 回退数字字符 int num; fscanf(fp, "%d", &num); printf("Parsed number: %d\n", num); } else if (c == '"') { parse_string(fp); // 进入字符串解析 } else { ungetc(c, fp); // 回退未知字符 parse_identifier(fp); } }
设计亮点:通过预读+回退实现多类型解析分支
场景2:二进制流标记检测
int find_magic(FILE *fp, const char *magic) { int match = 1; long pos = ftell(fp); for (const char *p = magic; *p; ++p) { int c = fgetc(fp); if (c != *p) { match = 0; // 关键点:精准回退已读取字节 fseek(fp, pos, SEEK_SET); break; } } return match; }
对比优势:相比单纯使用ungetc,fseek+ftell组合更适合二进制流回溯
高级技巧:缓冲区冲突测试
char buf; FILE *fp = fopen("test.txt", "r"); setvbuf(fp, buf, _IOFBF, sizeof(buf)); // 设置全缓冲 int c1 = fgetc(fp); // 触发缓冲填充 ungetc('X', fp); // 向缓冲区插入字符 // 验证缓冲区修改 printf("Current buffer: ["); for (int i=0; i<sizeof(buf); i++) { printf("%c ", (buf[i] == 0) ? '_' : buf[i]); } printf("]\n");
运行结果:缓冲区首字符被修改为X,展示库函数内部缓冲区操作机制
流状态劫持:动态修改输入内容
// 运行时替换输入流中的敏感词 void filter_swear_words(FILE *fp) { int c; while ((c = fgetc(fp)) != EOF) { if (c == 'F') { // 检测敏感词起始字符 ungetc('?', fp); // 替换为无害字符 ungetc('?', fp); // 双写保持字符数一致 break; } putchar(c); } }
独特价值:
实现「字符级输入流动态重写」,适用于实时过滤系统(如聊天室脏话过滤),相比传统先读后处理的方案节省内存。
跨平台差异警告表
行为 |
Linux (glibc) |
Windows (MSVCRT) |
最大回退次数 |
4 (默认缓冲大小) |
1 (严格遵守C99) |
回退EOF |
允许 |
导致未定义行为 |
混合fseek后的有效性 |
失效 |
部分版本保留回退字符 |
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/175770.html