C++中如何实现不同语言字符返回统一长度?
我是一名C++初学者,正在学习动态分配。我希望无论输入韩语还是英语,都能得到统一的字符串长度,因此使用了wstring。但wstring.length()或wstring.size()仍按多字节机制计算,韩语字符返回2字节,英语字符返回1字节。请问如何实现不同语言字符返回统一的单位长度?
代码示例
void ex_03() { std::wcout << endl<< "Your Input : " << endl; wstring inputval; std::wcin >> inputval; int slength = inputval.size(); wchar_t* p = new wchar_t[slength]; int cnt = 0; for (wchar_t a : inputval) { p[cnt] = a; cnt++; } std::wcout << endl << "your Input length : " << slength << endl << "String is : " << p; delete[] p; }
我原本以为用wstring可以解决,但结果不符合预期。
解决方案
首先要明确:wstring的size()返回的是wchar_t元素的数量,而非**Unicode码点(即视觉上的单个字符)**的数量。在Windows平台,wchar_t是2字节,对应UTF-16编码——韩语字符属于基本多语言平面(BMP),每个字符占1个wchar_t;但如果是超出BMP的字符(如某些emoji)会占2个wchar_t。而在Linux平台,wchar_t是4字节,对应UTF-32,此时size()直接等于码点数量。要跨平台实现统一的字符计数,核心是统计Unicode码点的数量,以下是两种可行方法:
方法1:转换为UTF-32字符串(u32string)
std::u32string的每个元素是char32_t,对应一个Unicode码点,因此它的size()就是视觉字符的数量。可以通过编码转换将wstring转为u32string:
#include <locale> #include <codecvt> #include <string> #include <iostream> std::u32string wstring_to_u32string(const std::wstring& ws) { // 先将wstring转为UTF-8 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf16_conv; std::string utf8_str = utf16_conv.to_bytes(ws); // 再将UTF-8转为UTF-32的u32string std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> utf32_conv; return utf32_conv.from_bytes(utf8_str); } void ex_03() { std::wcout << L"\nYour Input : " << std::endl; std::wstring inputval; std::wcin >> inputval; // 统计Unicode码点数量 std::u32string u32_input = wstring_to_u32string(inputval); size_t codepoint_count = u32_input.size(); // 注意:动态分配时要预留终止符的位置 wchar_t* p = new wchar_t[inputval.size() + 1]; int cnt = 0; for (wchar_t a : inputval) { p[cnt] = a; cnt++; } p[cnt] = L'\0'; // 添加字符串终止符,避免输出乱码 std::wcout << L"\nyour Input length (Unicode codepoints): " << codepoint_count << L"\nString is : " << p << std::endl; delete[] p; }
方法2:直接遍历UTF-16字符串统计码点
如果是Windows平台(wstring为UTF-16),可以通过判断代理对来统计码点:UTF-16中,高代理范围是0xD800~0xDBFF,低代理是0xDC00~0xDFFF,遇到高代理时需要跳过下一个低代理,算作一个码点。
#include <iostream> #include <string> size_t count_unicode_codepoints(const std::wstring& ws) { size_t count = 0; for (size_t i = 0; i < ws.size(); ++i) { wchar_t c = ws[i]; // 判断是否是高代理,若是则跳过下一个低代理 if (c >= 0xD800 && c <= 0xDBFF) { ++i; } ++count; } return count; } void ex_03() { std::wcout << L"\nYour Input : " << std::endl; std::wstring inputval; std::wcin >> inputval; size_t codepoint_count = count_unicode_codepoints(inputval); wchar_t* p = new wchar_t[inputval.size() + 1]; int cnt = 0; for (wchar_t a : inputval) { p[cnt] = a; cnt++; } p[cnt] = L'\0'; std::wcout << L"\nyour Input length (Unicode codepoints): " << codepoint_count << L"\nString is : " << p << std::endl; delete[] p; }
额外提示
你的原代码中动态分配wchar_t数组时没有预留终止符位置,这会导致std::wcout输出时可能出现乱码,记得添加p[cnt] = L'\0';。
内容的提问来源于stack exchange,提问作者Nothing

