std::string是char字节序列,无内置编码语义;UTF-8可直接使用,GBK/UTF-16等需经宽字符中转;Windows用MultiByteToWideChar+WideCharToMultiByte转换;跨平台推荐ICU或Boost.Locale;注意null terminator和错误检查。

std::string 默认就是 UTF-8 字节序列,但不等于“任意编码”
很多开发者误以为 std::string 是“通用字符串容器”,其实它只是 char 序列,没有内置编码语义。如果你拿到的是 UTF-8 编码的 std::string,那它本身已经是字节数组;但若原始文本是 GBK、Shift-JIS 或 UTF-16,直接取 .data() 会得到错误字节流。
关键判断点:你手里的字符串内容来源是什么?
- 来自文件(且你知道其 BOM 或声明编码)→ 需先解码为 Unicode(如
std::u32string),再重编码 - 来自 Windows API(如
GetWindowTextA)→ 很可能是当前系统 ANSI(如 GBK)→ 可用MultiByteToWideChar+WideCharToMultiByte - 来自网络或跨平台库(如 cpr、nlohmann_json)→ 大概率已是 UTF-8 → 直接用
str.data()和str.size()
Windows 下转 GBK / GB2312:用 WideCharToMultiByte
这是最常见需求——把宽字符(UTF-16)转成 GBK 字节。注意:不能跳过宽字符中间层,std::string 无法安全承载非 UTF-8 的多字节编码。
典型流程:
立即学习“C++免费学习笔记(深入)”;
- 用
MultiByteToWideChar(CP_UTF8, ...)把原始 UTF-8std::string转成std::wstring - 再用
WideCharToMultiByte(CP_ACP, ...)或WideCharToMultiByte(936, ...)(936 = GBK)转出目标字节 - 目标缓冲区长度必须用
WideCharToMultiByte(..., nullptr, 0, ...)先试探
示例(转 UTF-8 字符串为 GBK 字节数组):
std::string utf8_str = u8"你好"; int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, nullptr, 0); std::wstring wstr(wlen, L'\0'); MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, &wstr[0], wlen); int gbk_len = WideCharToMultiByte(936, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); std::vector<char> gbk_bytes(gbk_len); WideCharToMultiByte(936, 0, wstr.c_str(), -1, gbk_bytes.data(), gbk_len, nullptr, nullptr); // gbk_bytes 现在是 GBK 编码的字节
跨平台方案:用 ICU 或 std::text_encoding(C++23)
C++20 没有标准编码转换,C++23 引入了 <text_encoding>,但目前(GCC 14/Clang 18/MSVC 19.39)几乎无完整实现。生产环境仍需依赖第三方。
- ICU 库最成熟:
ucnv_convert支持 200+ 编码,但二进制体积大、API 繁琐 - Boost.Locale 提供封装:
boost::locale::conv::between,底层可选 ICU 或 iconv - Linux/macOS 可用
iconv()(POSIX 标准),但 Windows 需 MinGW 或额外链接
用 Boost.Locale 转 UTF-8 → Latin1 示例:
#include <boost/locale.hpp> std::string utf8 = "café"; std::string latin1 = boost::locale::conv::between(utf8, "ISO-8859-1", "UTF-8"); // latin1.data() 就是 ISO-8859-1 字节数组
别忘了空终止符和长度陷阱
很多函数(如 WideCharToMultiByte)默认写入 null terminator(当传入 -1 作源长度时)。这意味着返回的字节数组长度比实际内容多 1,且末尾是 \0。如果你要发 HTTP body 或写二进制文件,这个 \0 很可能造成协议错误。
- 检查是否传了
-1:它会让 API 自动加结尾\0;改用str.length()可避免 - 用
std::vector<char></char>存储结果,而不是std::string—— 后者对含\0的二进制数据不友好(.size()和.c_str()行为不一致) - 调试时用十六进制打印验证:
for(uint8_t b : bytes) printf("%02x ", b);
真正容易被忽略的,是编码转换链中每一步的错误处理:MultiByteToWideChar 返回 0 表示失败,但很多人没检查就继续调下一层——结果得到全零字节数组,还很难定位。


















