如何使用C++ ifstream读取文件中的空终止UTF-8和UTF-16字符串
现有代码的问题
你当前的实现思路方向是正确的,但存在几个明显缺陷:
- 未处理字节序问题:直接读取2字节赋值给
char16_t的逻辑默认了文件存储的UTF-16字节序和当前主机的字节序完全一致,如果文件是UTF-16 LE而主机是大端,或者反过来,读出来的字符会完全乱码。 - 未判断流读取状态:没有检查
binary.read()的返回结果,如果文件读取到末尾还没有遇到0x0000终止符,会读取无效内存值,触发未定义行为。 - 使用已弃用接口:
std::wstring_convert和std::codecvt_utf8_utf16从C++17开始已经被标准库弃用,在高版本编译器下会报警告,且后续标准可能移除该接口。 - 灵活性不足:函数直接将UTF-16转成UTF-8返回,如果业务需要保留原
std::u16string格式还需要额外修改。
正确的UTF-16字符串读取实现
读取UTF-16字符串的核心逻辑确实是按2字节为单位读取,直到遇到0x0000终止符,只需要补充字节序处理、异常判断即可,优化后的可参考实现如下:
#include <fstream> #include <string> #include <cstdint> // 辅助函数:将小端存储的2字节转为主机字节序,文件是大端的话替换为对应的be16_to_host即可 inline char16_t le16_to_host(const char* bytes) { return static_cast<char16_t>( (static_cast<uint8_t>(bytes[0])) | (static_cast<uint8_t>(bytes[1]) << 8) ); } // 读取原始UTF-16字符串 std::u16string binaryReader::read_u16str() { std::u16string result; char buf[2]; // 提前分配容量避免多次扩容,可根据实际场景调整预分配大小 result.reserve(64); while (binary.read(buf, 2)) { char16_t ch = le16_to_host(buf); if (ch == u'\0') { break; } result.push_back(ch); } return result; } // 轻量UTF-16转UTF-8实现(仅支持BMP平面字符,需支持代理对可自行扩展逻辑) std::string u16_to_u8(const std::u16string& u16_str) { std::string u8_str; u8_str.reserve(u16_str.size() * 3); for (char16_t ch : u16_str) { if (ch <= 0x7F) { u8_str += static_cast<char>(ch); } else if (ch <= 0x7FF) { u8_str += static_cast<char>(0xC0 | (ch >> 6)); u8_str += static_cast<char>(0x80 | (ch & 0x3F)); } else { u8_str += static_cast<char>(0xE0 | (ch >> 12)); u8_str += static_cast<char>(0x80 | ((ch >> 6) & 0x3F)); u8_str += static_cast<char>(0x80 | (ch & 0x3F)); } } return u8_str; }
额外注意:如果文件携带BOM,可先读取前2字节判断字节序,再动态选择对应的字节序转换函数即可。
内容的提问来源于stack exchange,提问作者file-tracer
相关产品推荐
相关产品推荐

