逻辑大小写比较是将全角字母(如A、a)与半角字母(如a、a)视为等价并忽略大小写的字符串比较方法;需先将全角ascii字符(u+ff01–u+ff5e)减去0xfee0转为半角,再对半角字符用std::tolower统一为小写。

什么是“逻辑大小写比较”?
所谓逻辑大小写比较,是指把全角字母(如 A、B、a、b)和半角字母(如 A、B、a、b)视为等价,并在忽略大小写前提下进行比较。C++ 标准库的 std::lexicographical_compare 或 operator 默认只按字节值比,<code>A(U+FF21)和 A(U+0041)完全不相等,更不会忽略大小写——所以必须自己处理。
如何统一转换全角/半角并忽略大小写?
核心思路是:对每个字符做两步预处理——先转半角(全角 ASCII 字符映射到对应半角),再转小写(或大写)。注意:仅对 ASCII 范围及常见全角拉丁字母做映射,其他 Unicode 字符(如汉字、希腊字母)保持原样,否则会出错。
- 全角转半角:检查字符是否在全角 ASCII 区间
0xFF01–0xFF5E(!–~),若是,则减去偏移量0xFEE0 - 大小写归一:对已转为半角的字符,用
std::tolower(需传入static_cast<unsigned char></unsigned>避免符号扩展) - 不建议直接用
std::toupper/std::tolower处理原始全角字符——它们对非 ASCII 字符行为未定义,且多数 C++ 标准库实现不支持全角
示例函数:
char32_t normalize_char(char32_t c) {
if (c >= 0xFF01 && c = 0x0041 && c
<h3>为什么不能用 std::locale 或 towlower?</h3>
<p><code>std::locale</code> 的 <code>std::use_facet<:ctype>></:ctype></code> 对 UTF-8 字符串无效;<code>towlower</code> 依赖当前 locale,而默认 "C" locale 只处理 ASCII,Windows 的 "Chinese" locale 也基本不支持全角拉丁字母映射。实测中,<code>towlower(L'A')</code> 返回仍是 <code>L'A'</code>,毫无效果。</p>
- 不要尝试用
std::wstring_convert+std::codecvt_utf8——已弃用,且无法解决全角/半角映射问题 - 不要对 UTF-8 字符串逐字节调用
std::tolower——会破坏多字节序列,引发 UB - 若输入是 UTF-8
std::string,必须先用第三方库(如 ICU、utf8cpp)或手写解码为std::u32string,再处理
性能与边界注意事项
每次比较都做 Unicode 码点转换,开销比原生字节比较高。如果高频调用,建议提前缓存归一化后的 std::u32string,而非每次实时计算。
- 全角标点(如 ,。?)不参与大小写转换,但它们的码点与半角标点(, . ?)不同,比较时仍会区分——这是预期行为,不是 bug
- 日文平假名/片假名(如 あ/ア)没有大小写概念,也不在全角 ASCII 范围内,
normalize_char不动它们,符合逻辑比较语义 - 某些字体或输入法生成的“伪全角”字符(如 U+200B 零宽空格)不属于全角 ASCII 区间,不会被转换,需额外清洗
真正麻烦的是混合编码场景:UTF-8 输入、GB2312 混入、甚至带 BOM 的文件——这时候光靠字符映射不够,得先做编码探测和转码。那已经超出“逻辑大小写比较”的范畴了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











