C++20 中如何正确实现UTF-8多字节字符串的回文校验功能
C++20 下兼容多字节与区域规则的回文判断实现
1. 兼容多字节Unicode字符的回文判断逻辑
之前直接反转std::u8string字节序列的方案,会破坏多字节Unicode码点的内部字节顺序,导致中文、Emoji等占2-4字节的字符反转后变成乱码,判断失效。
正确的实现思路是跳过字节维度,直接从字符串首尾逐次提取完整的Unicode码点做对称比较,不需要完全反转字符串,性能也更优,参考实现如下:
#include <string> #include <cuchar> #include <cstddef> #include <char_traits> bool is_palindrome_unicode(const std::u8string& str) { // 从左向右提取下一个有效Unicode码点 auto next_codepoint = [](const char8_t*& curr, const char8_t* end) -> char32_t { std::mbstate_t state{}; char32_t cp; const std::size_t len = std::mbrtoc32( &cp, reinterpret_cast<const char*>(curr), end - curr, &state ); // 遇到无效编码直接返回非法标记 if (len == static_cast<std::size_t>(-1) || len == static_cast<std::size_t>(-2)) { return 0xFFFFFFFF; } curr += len; return cp; }; // 从右向左提取前一个有效Unicode码点 auto prev_codepoint = [](const char8_t*& curr, const char8_t* start) -> char32_t { // 回退到当前码点的起始字节(UTF-8续字节固定为0x80-0xBF) while (curr > start && ((*curr & 0xC0) == 0x80)) { --curr; } std::mbstate_t state{}; char32_t cp; const std::size_t len = std::mbrtoc32( &cp, reinterpret_cast<const char*>(curr), (start + std::char_traits<char8_t>::length(reinterpret_cast<const char*>(start))) - curr, &state ); if (len == static_cast<std::size_t>(-1) || len == static_cast<std::size_t>(-2)) { return 0xFFFFFFFF; } --curr; return cp; }; const char8_t* left = str.data(); const char8_t* right = str.data() + str.size() - 1; while (left < right) { const char32_t l_cp = next_codepoint(left, str.data() + str.size()); const char32_t r_cp = prev_codepoint(right, str.data()); if (l_cp == 0xFFFFFFFF || r_cp == 0xFFFFFFFF || l_cp != r_cp) { return false; } } return true; }
如果需要处理带组合标记的字素簇(比如带重音的拉丁字符、ZWJ组合Emoji),C++20标准库目前没有内置字素分割能力,可搭配ICU等第三方Unicode库实现更精准的判断。
2. 适配区域设置的字符等价规则
要适配不同区域的字符等价规则(比如德语区ß等价于ss、法语区忽略重音差异、大小写不敏感等),需要先对字符做区域感知的归一化处理,再做等价比较,参考实现如下:
#include <string> #include <locale> #include <cuchar> #include <algorithm> bool is_palindrome_locale_aware(const std::u8string& str, const std::locale& loc = std::locale("")) { // 提取所有Unicode码点 std::u32string cp_seq; std::mbstate_t state{}; const char8_t* curr = str.data(); const char8_t* end = str.data() + str.size(); while (curr < end) { char32_t cp; const std::size_t len = std::mbrtoc32(&cp, reinterpret_cast<const char*>(curr), end - curr, &state); if (len == static_cast<std::size_t>(-1) || len == static_cast<std::size_t>(-2)) { return false; } // 按当前区域规则转换为大写归一化,消除大小写差异 cp = std::toupper(static_cast<wchar_t>(cp), loc); cp_seq.push_back(cp); curr += len; } // 对称比较归一化后的码点 return std::equal(cp_seq.begin(), cp_seq.end(), cp_seq.rbegin(), cp_seq.rend()); }
调用时传入对应区域的std::locale实例即可适配对应规则,比如传入std::locale("de_DE.UTF-8")即可启用德语区的字符等价规则。
注意不同操作系统的区域标识写法存在差异,跨平台场景下建议搭配ICU库使用,规则一致性更好。
内容的提问来源于stack exchange,提问作者Daniel Dearlove
相关产品推荐
相关产品推荐

