如何在C++中将指定编码的字符串转换为UTF-16宽字符串?
解决方案
存在多种工具和方法可以实现已知编码的std::string到UTF-16宽字符串的转换,std::wstring_convert确实无法直接通过编码名称字符串指定转换规则,但可以通过以下几种更实用的途径实现需求:
1. Windows平台:使用系统原生API
Windows系统的MultiByteToWideCharAPI直接支持指定代码页(GB-18030对应的代码页为CP_GB18030),无需依赖第三方库,转换效率和兼容性都有保障:
#include <string> #include <windows.h> std::wstring gb18030_to_utf16(const std::string& gb_str) { if (gb_str.empty()) return {}; // 计算所需UTF-16缓冲区大小 const int wide_buf_size = MultiByteToWideChar(CP_GB18030, 0, gb_str.c_str(), (int)gb_str.size(), nullptr, 0); if (wide_buf_size == 0) return {}; std::wstring utf16_str(wide_buf_size, 0); MultiByteToWideChar(CP_GB18030, 0, gb_str.c_str(), (int)gb_str.size(), utf16_str.data(), wide_buf_size); return utf16_str; }
2. 跨平台方案:使用第三方Unicode处理库
标准库对非UTF编码的支持有限,跨平台场景推荐使用成熟的第三方库,它们支持通过编码名称字符串指定转换规则:
ICU库(跨平台首选)
ICU是专业的Unicode处理库,支持几乎所有常见编码,可直接通过编码名(如"GB18030")完成转换:
#include <string> #include <unicode/ucnv.h> #include <unicode/ustring.h> std::wstring convert_to_utf16(const std::string& src_str, const char* src_encoding) { UErrorCode status = U_ZERO_ERROR; UConverter* converter = ucnv_open(src_encoding, &status); if (U_FAILURE(status)) return {}; // 计算所需UTF-16缓冲区大小 int32_t dest_len = ucnv_toUChars(converter, nullptr, 0, src_str.c_str(), (int32_t)src_str.size(), &status); if (status != U_BUFFER_OVERFLOW_ERROR) { ucnv_close(converter); return {}; } status = U_ZERO_ERROR; std::wstring dest_str(dest_len, 0); ucnv_toUChars(converter, reinterpret_cast<UChar*>(dest_str.data()), dest_len, src_str.c_str(), (int32_t)src_str.size(), &status); ucnv_close(converter); return U_SUCCESS(status) ? dest_str : std::wstring{}; } // 使用示例: // std::wstring utf16_str = convert_to_utf16(gb_str, "GB18030");
iconv库(POSIX系统常用)
iconv是POSIX标准的编码转换库,同样支持通过编码名称指定转换规则:
#include <string> #include <iconv.h> #include <cstring> std::wstring convert_to_utf16(const std::string& src_str, const char* src_encoding) { // 根据系统字节序选择UTF-16LE或UTF-16BE iconv_t converter = iconv_open("UTF-16LE", src_encoding); if (converter == (iconv_t)-1) return {}; char* in_buf = const_cast<char*>(src_str.c_str()); size_t in_remaining = src_str.size(); const size_t out_buf_size = in_remaining * 2; char* out_buf = new char[out_buf_size]; char* out_start = out_buf; size_t out_remaining = out_buf_size; const size_t convert_result = iconv(converter, &in_buf, &in_remaining, &out_buf, &out_remaining); const bool success = (convert_result != (size_t)-1) && (in_remaining == 0); std::wstring dest_str; if (success) { const size_t converted_len = out_buf_size - out_remaining; dest_str = std::wstring(reinterpret_cast<wchar_t*>(out_start), converted_len / sizeof(wchar_t)); } delete[] out_start; iconv_close(converter); return dest_str; }
关于std::wstring_convert的补充说明
标准库的std::wstring_convert需要配合std::codecvt的特化版本使用,但标准库仅提供了UTF相关的特化(如codecvt_utf8_utf16),没有内置GB-18030这类编码的支持。如果一定要基于它实现,需要自行编写对应的codecvt facet,这一过程繁琐且易出错,远不如上述方案高效可靠。此外,C++17开始std::wstring_convert和std::codecvt已被标记为弃用,更不推荐在新项目中使用。
内容的提问来源于stack exchange,提问作者Mikhail Svetlov
相关产品推荐
相关产品推荐

