c++oding="utf-8" ?>
std::tolower在utf-8字符串上会崩,因其将多字节utf-8字符误作独立ascii字节处理,导致乱码或非法序列;标准库无可靠unicode大小写转换支持,需先utf-8解码、归一化、全折叠、再编码。

为什么 std::tolower 在 UTF-8 字符串上会崩?
直接对 UTF-8 字节流调用 std::tolower(或 std::toupper)是危险的:它把每个字节当 ASCII 处理,而 UTF-8 的多字节字符(如 é、中文、α)会被拆成多个独立字节,各自转大小写,结果不是乱码就是非法序列。比如 "café" 中的 é(UTF-8 编码为 0xc3 0xa9)会被错当成两个 Latin-1 字符处理,输出不可逆。
用 std::locale + std::codecvt_utf8?别试了
std::codecvt_utf8 在 C++17 被标记为 deprecated,C++20 彻底移除;std::locale 的 facet(如 std::use_facet<:ctype>></:ctype>)虽支持 Unicode,但标准库不保证 UTF-8 与 char32_t 的转换正确性,且 Linux/macOS 的 glibc/libc++ 实现常返回空 locale 或静默失败。实测中 std::toupper('é', std::locale("")) 可能返回原值或 0,毫无可移植性。
真正靠谱的做法:先 UTF-8 解码 → char32_t → case fold → UTF-8 编码
逻辑大小写比较(case-insensitive comparison)本质是“case folding”,不是简单转小写。Unicode 规范要求使用 full case fold(如 ß → ss,İ → i̇),而 C++ 标准库完全不提供此能力。必须依赖成熟 Unicode 库:
- 推荐 ICU(
icu::UnicodeString::caseCompare+UNORM_NFC归一化),但体积大、编译复杂 - 轻量级首选 utf8cpp + 手动查表:用
utf8::next()解码为char32_t,再查 Unicode CaseFolding.txt 生成的映射表(注意包含status = C行,即 full case fold) - 若仅需 ASCII + 基本拉丁扩展(如西欧语言),可用
libiconv+iconv("UTF-8", "UTF-32LE")解码,再用 ICU-lite 或自建折叠表
示例关键步骤(伪代码):
std::string fold_case(const std::string& s) {
std::u32string u32;
utf8::utf8to32(s.begin(), s.end(), std::back_inserter(u32));
std::u32string folded;
for (char32_t cp : u32) {
auto it = case_fold_map.find(cp);
if (it != case_fold_map.end()) {
for (char32_t f : it->second) folded.push_back(f);
} else {
folded.push_back(cp);
}
}
std::string out;
utf8::utf32to8(folded.begin(), folded.end(), std::back_inserter(out));
return out;
}
// 比较时:fold_case(a) == fold_case(b)
别忽略 normalization(归一化)这步
同一个字符可能有多种 UTF-8 表示:比如 "ñ" 可以是单个预组字符 U+00F1,也可以是 U+006E + U+0303(n + 组合波浪符)。case folding 前必须做 NFC 归一化,否则 fold_case("n\u0303") 和 fold_case("\u00f1") 结果不同。ICU 提供 unorm_normalize();轻量方案可用 ICU 的开源 normalization 表 或 utf8proc 库(utf8proc_decompose + utf8proc_recombine)。
没做 NFC 的 case fold 比较,在带重音符号、emoji 或组合字符的场景下,几乎必然出错——这点比大小写转换本身更容易被跳过。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











