应使用 _wfopen 而非 fopen 处理宽字符文件:它接受 wchar_t* 路径和模式(如 L"wt, ccs=UTF-16LE"),支持 BOM 与编码协商;std::wofstream 需手动写入 \xFF\xFE BOM 才能正确显示中文。

用 fopen 打开宽字符文件会直接失败
Windows 上用 fopen("中文.txt", "w") 写入 wchar_t 数据,文件内容大概率是乱码或空——因为 fopen 默认按窄字符(char)处理,不识别 BOM,也不做 UTF-16 编码转换。
真正能安全读写宽字符的,是 Microsoft 提供的宽字符版 C 标准库函数:_wfopen(注意开头下划线和 w 前缀),它接受 const wchar_t* 路径和模式,并在内部处理编码协商。
-
_wfopen在 Windows 下支持"wt, ccs=UTF-16LE"这类编码标识,Linux/macOS 不支持,别跨平台硬搬 - 路径必须是
wchar_t[],不能传std::string.c_str(),否则崩溃或静默失败 - 模式字符串也得是宽字符串,比如
L"wt, ccs=UTF-16LE",漏掉L前缀是常见低级错误
std::wofstream 默认不写 BOM,中文全变方块
用 std::wofstream 直接 L"你好",生成的文件在记事本里打开大概率显示为“??”,因为流默认以本地宽字符编码(通常是 UTF-16 但无 BOM)写入,而 Windows 记事本只靠 BOM 判断是否 UTF-16。
必须手动写入 BOM 并设置流的 locale,否则编码不可靠:
立即学习“C++免费学习笔记(深入)”;
- 先用
std::locale::global(std::locale(""))绑定系统 locale(仅 Windows 有效) - 再对流调用
imbue(),例如out.imbue(std::locale(".1200"))(Windows codepage 1200 = UTF-16LE) - 最稳妥方式:用
out.rdbuf()->pubsetbuf(nullptr, 0)禁用缓冲后,先写\xFF\xFE(UTF-16LE BOM),再
跨平台读宽字符文件,std::wifstream 在 Linux 下基本失效
Linux 的 libc 对宽字符文件流支持极弱:std::wifstream 读 UTF-8 文件会把每个字节当一个 wchar_t,结果全是错码;读 UTF-16 更是直接失败——因为 glibc 的 codecvt 已被弃用,且无默认 UTF-16 支持。
实际可行方案只有两个:
- 统一用
std::ifstream按std::vector<char></char>读原始字节,自己检测 BOM(\xFF\xFE/\xFE\xFF/\xEF\xBB\xBF),再用std::mbstowcs或第三方库(如 ICU、utf8cpp)转成std::wstring - 彻底放弃
wchar_t,改用std::u16string+std::ifstream+ 手动 UTF-16 解码,控制权更稳 - 别依赖
std::codecvt_utf8_utf16:C++17 已标为 deprecated,GCC/Clang 新版本编译直接报错
写入时用 fputws 比 fwprintf 更干净
如果只是写纯文本(不含格式化),fputws 是比 fwprintf 更轻量、更少坑的选择——它不解析格式符,不会因漏传参数导致未定义行为,也不会把 % 当指令误处理。
-
fputws(L"路径含%符号.txt", fp)安全;fwprintf(fp, L"路径含%符号.txt")会崩 -
fputws自动在末尾加换行?不会。它严格输出传入的wchar_t*,换行得自己加L"\n" - 返回值是
int,成功时返回非负值,失败返回EOF,务必检查,尤其写网络文件系统或权限受限路径时











