字符串编码需依赖bom等外部信息而非猜测,utf-8 bom为0xef 0xbb 0xbf,utf-16 be为0xfe 0xff,le为0xff 0xfe,utf-32 be为0x00 0x00 0xfe 0xff,le为0xff 0xfe 0x00 0x00。

字符串本身不携带编码信息,必须依赖上下文或元数据
C++ 标准库里的 std::string 或 std::u8string 只是字节容器,不记录“这是 UTF-8”还是“这是 GBK”。所谓“判断编码”,实际是**对一串字节做启发式推测**,结果不可靠,仅适用于已知来源较干净的场景(如读取无 BOM 的文本文件)。生产环境应优先通过协议头、BOM、配置项等外部信息明确指定编码,而非靠猜。
用 BOM 快速识别 UTF-8 / UTF-16 / UTF-32
BOM(Byte Order Mark)是最可靠且低成本的判断依据。它位于文件或字符串开头,长度固定,可直接检查前几个字节:
- UTF-8 BOM 是
0xEF 0xBB 0xBF(3 字节),出现即基本可断定为 UTF-8 - UTF-16 BE BOM 是
0xFE 0xFF(2 字节);UTF-16 LE 是0xFF 0xFE - UTF-32 BE BOM 是
0x00 0x00 0xFE 0xFF;LE 是0xFF 0xFE 0x00 0x00
注意:std::string 若来自文件读取,需确保以二进制模式打开(std::ios::binary),否则 Windows 下可能被 \r\n 转换干扰字节序列。
用 ICU 或 iconv 做概率性检测(不推荐默认启用)
若无 BOM,且必须尝试识别,可用成熟库辅助。ICU 的 ucsdet_open 和 iconv 的 iconv_open + 错误试探都支持,但要注意:
- ICU 的
ucsdet_detect返回置信度,但对短文本( - 中文常见编码(GBK/GB2312/Big5)与 UTF-8 在 ASCII 区完全重叠,单靠字节无法区分
- 某些检测逻辑会把含大量 ASCII 的 UTF-8 当作 ISO-8859-1,尤其当文本不含非 ASCII 字符时
示例(ICU 简化调用):
UCharsetDetector* cd = ucsdet_open(&status);<br>ucsdet_setText(cd, bytes.data(), bytes.size(), &status);<br>const UCharsetMatch* match = ucsdet_detect(cd, &status);<br>const char* name = ucsdet_getName(match, &status); // 如 "UTF-8", "GBK"
避免踩坑:不要用 strlen 或遍历判断宽字符
常见错误是认为“能被 std::wstring_convert<:codecvt_utf8></:codecvt_utf8> 解码就是 UTF-8”——这会导致崩溃或静默截断。更糟的是用 std::mbstowcs 配合 setlocale,它依赖当前 locale 设置,而 locale 不一定匹配源文本编码(例如系统 locale 是 en_US.UTF-8,但文本实为 GBK)。
真正安全的做法只有两种:
- 明确约定并传递编码(如 HTTP
Content-Type: text/plain; charset=gbk) - 强制要求输入带 BOM,或使用自描述格式(如 JSON 要求 UTF-8,XML 声明
<?xml version="1.0" encoding="GBK"?>)
所有“自动识别”都是妥协方案,一旦文本混杂、截断或含控制字符,结果就不可信。别让编码探测成为你程序里最脆弱的一环。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











