CSV写入Unicode前必须加BOM,否则Excel打开含中文、日文等字符的UTF-8文件会乱码;因Windows记事本和Excel默认将无BOM的UTF-8当作ANSI处理;解决方法是用binary模式打开文件,并先写入UTF-8 BOM(0xEF 0xBB 0xBF)。

CSV写入Unicode前必须加BOM
不加BOM,Excel打开含中文、日文等Unicode字符的CSV时大概率显示乱码,哪怕文件实际编码是UTF-8。Windows记事本和Excel默认把无BOM的UTF-8当作ANSI处理,这是最常踩的坑。
解决方法很简单:在写入任何内容前,先向std::ofstream写入UTF-8 BOM(0xEF 0xBB 0xBF):
std::ofstream file("data.csv", std::ios::binary); // 注意:必须用binary模式
file << ""; // 写入BOM
file << "姓名,城市
张三,上海
";
关键点:
-
std::ios::binary必须显式指定,否则Windows下文本模式会把转成,还可能干扰BOM字节 - BOM只能写一次,且必须是文件开头的前3个字节
- 不要用
u8"..."字符串字面量直接拼接BOM——编译器可能在前面再加BOM,导致重复
std::wstring + std::codecvt_utf8不推荐
想用std::wstring存中文再转UTF-8?别走这条路。C++17起std::codecvt_utf8已被弃用,MSVC和Clang最新版编译会警告甚至报错,而且跨平台行为不一致(比如Linux下std::codecvt_utf8对某些代理对处理不可靠)。
立即学习“C++免费学习笔记(深入)”;
更稳妥的做法是:全程用std::string,确保源字符串已是UTF-8编码:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 字符串字面量加
u8"张三"前缀(C++11起支持) - 从UTF-8文件/网络读取的数据,直接保持为
std::string - 避免用
std::wcout或std::wstring_convert做中间转换
字段含逗号、换行、引号时必须正确转义
Unicode字符本身不影响CSV语法,但含中文的字段更容易出现逗号、换行符(比如地址栏填了“上海市 浦东新区”),这时不转义会导致解析错行。
CSV规范要求:只要字段含,、"或
,就必须用双引号包裹,且字段内原有的"要变成""(两个连续双引号):
// 正确转义示例 file << "姓名,备注 "; file << ""张三","地址:上海""浦东""新区\n电话:13800138000" ";
注意:
- 不要手动拼接引号和转义——容易漏掉边界情况,建议封装一个
csv_escape()函数 - 换行符
在字段内是合法的,但必须被引号包裹,否则CSV解析器会认为是新行 - UTF-8编码的Unicode字符(如
上海)本身不需要额外编码,直接写入即可
第三方库如csv-parser不自动处理BOM
像csv-parser(by ben-strasser)这类轻量库只负责解析结构,完全不管BOM。如果你用它生成CSV,它不会帮你加BOM;如果你用它读CSV,它也不会跳过BOM——你得自己处理。
实操建议:
- 写CSV时:用
std::ofstream开binary模式,手动写BOM,再调用库的write_row()之类接口 - 读CSV时:先用
std::ifstream读前3字节,判断是否0xEF 0xBB 0xBF,是则跳过,再把剩余流交给库解析 - 别依赖库的“UTF-8支持”宣传语——多数库只保证不破坏字节流,BOM和编码识别得自己兜底
BOM位置、二进制模式、字段转义这三点没对齐,Unicode CSV在Windows上基本等于废文件。尤其BOM,不是可选项,是Excel能认出UTF-8的唯一线索。

















