C语言fscanf读取文件首内容及空格失败、输出乱码问题咨询
先澄清核心认知错误
不存在「fopen读模式必须搭配先写模式调用才能正常工作」的规则。只要目标文件存在、路径正确、fopen返回有效文件指针,直接以读模式打开文件完全可以正常读取。
你之前观察到的「先开写再开读才正常」的现象,本质是你直接开读时,要么文件不存在、要么文件路径和程序运行工作目录不匹配,导致fopen返回NULL空指针,你又没有做返回值校验,直接操作空指针触发未定义行为才会运行异常;而你先以写模式打开时,"w"/"w+"模式会自动创建不存在的文件,后续再开读自然能找到文件,但写模式打开时会直接清空文件原有内容,本身就是错误用法。
你提供的代码存在3个直接导致乱码的错误
1. 未校验fopen返回值,且打开模式选择不当
你使用的"r+"是读写模式,要求目标文件必须预先存在,不会自动创建文件。如果Test.txt没有放在程序运行的当前工作目录(多数IDE的默认运行工作目录是编译后exe所在目录,不是源码存放目录),fopen会直接返回NULL,后续对空指针执行fscanf操作必然出现未定义行为,输出乱码是典型表现。
补充:如果你只需要读取文件内容,不需要写入,直接用
"r"模式即可,不需要用"r+"。
2. fscanf格式符使用完全错误
你写的格式串存在两个致命问题:
- 格式符中的
*是赋值抑制符,作用是匹配对应内容但直接丢弃,不写入任何变量。你在name、hobby对应的位置写了%*s,意味着这两个字段的内容根本不会被写入name、hobby数组,数组里是未初始化的随机值,打印出来自然是乱码。 - 默认的
%s格式符匹配规则是:从第一个非空白字符开始读取,遇到任意空白字符(空格、换行、制表符)就停止匹配,天然无法读取带空格的内容。你Test.txt里Name行的This is a test、Hobby行的Play games都包含空格,用%s只能读到第一个空格前的内容。
3. fscanf传参类型不匹配
读取字符串时,数组名会自动退化为对应首元素指针,直接传入name、hobby即可,你写的&name、&hobby是指向整个数组的指针,类型和fscanf要求的char*不匹配,同样会触发未定义行为。
另外你的代码没有引入<stdio.h>头文件,在部分编译环境下也会导致函数调用异常。
修正后可正常运行的代码
要读取带空格的整行内容,可以使用%[^\n]格式符,含义是匹配所有非换行符的字符,直到遇到换行才停止,同时要注意限制读取长度避免缓冲区溢出,读完每行后用%*c消费掉行尾的换行符,避免影响后续字段匹配。
#include <stdio.h> int main() { FILE *fp; // 只读场景用r模式即可 fp = fopen("Test.txt", "r"); // 必须做返回值校验 if (fp == NULL) { printf("文件打开失败,请确认Test.txt位于程序运行工作目录\n"); return 1; } char name[100], hobby[100]; int age; // 修正格式符:99是最大读取长度,预留1字节存字符串结束符\0,%*c吃掉行尾换行 int match_count = fscanf(fp, "Name:%99[^\n]%*cAge:%d%*cHobby:%99[^\n]", name, &age, hobby); // 校验匹配结果是否符合预期 if (match_count != 3) { printf("文件内容格式与预期不匹配\n"); fclose(fp); return 1; } printf("\nScan Result\n%s\n%d\n%s\n", name, age, hobby); fclose(fp); return 0; }
运行后会正常输出:
Scan Result This is a test 21 Play games
对你两个问题的直接回应
- fopen读模式本身可以直接使用,不需要提前调用写模式打开。你之前遇到的异常全部来自未校验fopen返回值、文件路径不匹配导致的空指针操作,和fopen读模式本身无关。
%s格式符天生不支持读取带空格的内容,要读取空格需要使用%[^\n]类的字符集匹配格式;你之前用的%*s本身就是丢弃匹配结果的用法,自然不会把内容写入变量,不可能生效。
内容的提问来源于stack exchange,提问作者LearnerC

