fgetws读取UTF-8文件时每个字符后出现\0的问题排查与解决
fgetws读取UTF-8文件时每个字符后出现\0的问题排查与解决
看起来你踩了一个典型的「宽字符/窄字符混用」的坑!你看到的每个字符后面跟着\0的诡异现象,根源其实很明确——你用了宽字符读取函数fgetws,但却把结果存到了窄字符数组char tempstr[]里,直接导致了数据存储格式不匹配。
问题根源拆解
在Windows平台下,fgetws是专门读取宽字符流的函数,它会把文件内容解析为wchar_t类型的宽字符(Windows里wchar_t通常是2字节的UTF-16编码)。对于ASCII字符来说,每个宽字符会被存为「字符字节 + 0字节」的形式,比如"R"就是0x52 0x00,"e"是0x65 0x00。
但你的tempstr是char类型的窄字符数组,当fgetws把宽字符数据写入这个数组时,就会把每个宽字符的两个字节依次塞进去——这就正好造成了你调试时看到的:'R'、'\0'、'e'、'\0'...交替出现的情况,最后自然只能存下单个字符。
针对性解决方案
根据你现有代码的需求(用char数组存储UTF-8字符串),给你两种可行的修复方向:
方案1:改用窄字符读取函数(最适配你的现有逻辑)
既然你的charlist是char类型的二维数组,直接用窄字符读取流程更简单,不需要引入宽字符的复杂处理:
- 替换
fgetws为fgets:fgets是窄字符版本的行读取函数,完美匹配char数组的存储类型。 - 优化循环条件:把依赖
feof的循环改成以fgets的返回值为判断依据(避免feof的经典陷阱:读取到文件末尾后还会多执行一次循环)。 - 确认文件打开参数:你用的
"r, ccs=UTF-8"在窄字符模式下,会自动把UTF-8编码转成系统当前的ANSI编码;如果想直接保留UTF-8原始字节(比如要支持非ASCII的Unicode字符),可以改成"r"(去掉ccs=UTF-8参数),这样fgets会直接读取UTF-8的字节序列到char数组里。
修改后的核心代码示例:
int load_charlist() { errno_t err; char tempstr[48]; // 保持原大小即可 int i = 0; err = fopen_s(&inputfile, "Saves//charlist.dat", "r, ccs=UTF-8"); // 或"r"以直接读取UTF-8字节 if (!inputfile) { set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); allegro_message("Can't open Saves\\charlist.dat. Exiting."); return 1; } charcount = 0; // 用fgets的返回值作为循环条件,更可靠 while (fgets(tempstr, _countof(tempstr), inputfile) != NULL) { trim_trailing_chars(tempstr, "\r\n"); if (strcmp(tempstr, "none") != 0) { strcpy_s(charlist[i], _countof(charlist[i]), tempstr); ++charcount; i++; // 加个边界检查,避免数组越界 if (i >= ML_CHARCNT) break; } } // 检查读取过程中是否出现错误 if (ferror(inputfile)) { allegro_message("Error reading charlist.dat."); } err = fclose(inputfile); return 0; // 记得返回0表示成功 }
方案2:全流程改用宽字符处理(如需支持复杂Unicode场景)
如果你后续需要处理非ASCII的Unicode字符(比如中文、日文),可以把整个字符串处理流程改成宽字符模式:
- 把
tempstr改成wchar_t tempstr[48]; - 把
charlist改成wchar_t charlist[ML_CHARCNT][ML_CHARNAME]; - 调整
trim_trailing_chars为宽字符版本:
void trim_trailing_chars(wchar_t* str, const wchar_t* chars_to_trim) { str[wcscspn(str, chars_to_trim)] = 0; }
- 字符串比较用
wcscmp,字符串复制用wcscpy_s,全部替换为宽字符版本的函数。 - 保持
fgetws的使用,此时tempstr是wchar_t数组,和函数要求完全匹配。
额外小提示
- 原代码中
while (!feof(inputfile) && !ferror(inputfile))的写法容易触发最后一行重复读取的问题,因为feof只有在尝试读取超过文件末尾后才会被置位,用fgets/fgetws的返回值作为循环条件更稳妥。 - 记得给
charcount和i加边界检查,避免超过ML_CHARCNT的上限导致数组越界。
内容来源于stack exchange
相关产品推荐
相关产品推荐

