C语言中使用fscanf区分存储double或字符串值的问题
C语言中使用fscanf区分存储double或字符串值的问题
我来帮你分析下你遇到的问题,核心原因是fscanf在匹配double类型失败时的行为和你预期的不一样,咱们一步步拆解:
问题根源
你当前的代码逻辑是先尝试用%lf读取浮点数,失败后再用%31s读取字符串,但fscanf处理%lf时的匹配规则会坑到你:
当fscanf("%lf", num)遇到类似+这样的符号(后面没有数字)时,它会先消耗掉这个符号,然后发现后续没有有效数字,于是返回0。此时文件指针已经移动到了符号后面的位置,后续调用%31s时,%s会自动跳过空白字符,直接读取后面的数字(比如你的例子里的3),导致原本的操作符+被彻底跳过,完全丢失。
解决方案
要解决这个问题,我们需要在尝试读取double之前,先记录当前文件指针的位置。如果读取失败(返回0),就把指针回溯到原来的位置,再尝试读取字符串。这样就能保证读取操作符时,不会跳过那些被%lf错误消耗的字符。
修改后的代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> /* A) Tries to read a double from the file and asign its value to the double pointed by num. If it was possible returns 1. B) In other case, tries to read a string up to 31 chars and stores its value on the string pointed by op, if it was possible returns 2. C) In any other case it returns 0 (end of file, etc)*/ int num_or_op(FILE* f, double *num, char *op) { // 记录当前文件指针位置,用于回溯 long current_pos = ftell(f); if (current_pos == -1) { // 获取位置失败,直接返回0 return 0; } int read = fscanf(f, "%lf", num); if (read == 1) { // 成功读取double,返回1 return 1; } else if (read == -1) { // 到达EOF或读取错误,返回0 return 0; } else { // 读取double失败,回溯到之前的位置 if (fseek(f, current_pos, SEEK_SET) != 0) { return 0; } // 尝试读取字符串 int aux = fscanf(f, "%31s", op); if (aux == 1) { return 2; } else { // 读取字符串失败(EOF或错误),返回0 return 0; } } }
代码解释
- 指针回溯:用
ftell记录读取double前的文件位置,失败时用fseek把指针移回去,确保后续读取字符串时能拿到原本的字符。 - 错误处理:增加了对
ftell和fseek失败的判断,让代码更健壮。 - 逻辑对齐:严格按照题目要求的A→B→C顺序执行,没有偏离要求的读取方式。
用你的测试输入1 2 + 3来验证:
- 前两次调用会成功读取1和2,返回1;
- 第三次调用尝试读取
+作为double失败,回溯指针后读取+作为字符串,返回2; - 第四次调用读取3,返回1。
完全符合你的预期。
备注:内容来源于stack exchange,提问作者ELiJuY
相关产品推荐
相关产品推荐

