三类映射表
Unicode 用三套数据描述大小写:简单映射(每个码点对应一个码点,多数情况)、特殊映射(可能一对多,且部分规则依赖语言,例如德语小写字母变大写后变成两个字母、希腊语词尾 sigma 依上下文选择形态)、大小写折叠(专为「忽略大小写比较」设计,把字符折叠成可比较形式)。因此「转成大写」与「忽略大小写比较」是两件事:前者改变内容用于展示或规范,后者应使用折叠规则。此外不少字符没有大小写概念,汉字、数字、符号都属此类。
基本信息
| 简单映射 | 一码点对一码点,覆盖大多数拉丁与希腊、西里尔字符 |
|---|---|
| 特殊映射 | 存在一对多与语言相关规则(长度可能改变) |
| 大小写折叠 | 用于忽略大小写比较的专门映射,常与 lower 结果不同 |
| 语言敏感 | 土耳其语与阿塞拜疆语的 i/I 映射与其他语言不同 |
| 上下文敏感 | 希腊语 sigma 的词尾形态由位置决定 |
| 无大小写字符 | 汉字、数字、符号等类别不参与大小写转换 |
典型使用场景
- 用户名与邮箱比对:不敏感比较应使用折叠规则而非简单小写
- 搜索与索引:查询词与索引内容统一折叠形式,保证召回一致
- 数据规范化:对外展示统一大小写形态,注意长度可能改变
- 标识符校验:转换后可能与既有数据冲突,需要唯一性策略
- 国际化界面:不同语言环境的大小写习惯不同,避免硬编码规则
映射行为示例
映射行为示例
python -c "print('ß'.upper(), 'ß'.casefold(), len('ß'.upper()))" # SS ss 2:一对多映射
python -c "print('Straße'.casefold() == 'STRASSE'.casefold())" # True:折叠用于不敏感比较
python -c "print('İ'.lower(), 'i'.upper())" # 土耳其语相关的差异案例
python -c "print('中文ABC'.upper(), '中文ABC'.casefold())" # 汉字不参与映射
python -c "print('hello world'.title(), \"don't\".title())" # title 会把撇号后的字母也大写
# 不敏感比较的正确写法:casefold 两侧后再比较操作与语义
| 操作 | 用途 | 注意点 |
|---|---|---|
| upper | 转大写,用于展示或规范 | 可能改变长度(一对多映射) |
| lower | 转小写,用于展示或规范 | 与折叠结果不等价 |
| casefold | 为不敏感比较做折叠 | 比较应使用它而不是 lower |
| title / capitalize | 词首大写 | 对缩写、撇号与连字符的处理常与预期不符 |
| locale 敏感转换 | 按语言规则转换 | 需显式指定语言,默认值依实现而不同 |
长度会变:「一个字符变大写还是一个字符」在 Unicode 下不成立。凡是先转换再按位置索引、截断或截取子串的逻辑,都要考虑转换前后长度不同带来的错位。
语言相关的映射容易造成跨端不一致:某些语言环境下字母 i 的大小写规则与其他语言不同,同一份代码在不同服务器或用户语言下产出不同结果。涉及标识符时应显式指定语言或使用不敏感折叠而非语言相关转换。
「忽略大小写比较」还可能与归一化和视觉相似字符叠加:安全敏感场景需要先归一化、再折叠、再校验,并配合字符白名单,不能只做一次小写转换就认为等价。