转换命名风格之前,先定义词的边界
不同系统可能使用驼峰、下划线或短横线组织名称。看起来只需要替换几个字符,但连续大写、开头分隔符和重复空白都会影响结果。
因此,命名转换应先确定输入允许哪些形式,如何识别单词边界,以及输出采用什么风格。不要让不同调用位置各自进行一次不完整的替换,最后得到多个含义相近却不相同的键。
下划线、连字符、空格以及大小写变化,都可能被理解为分词信号。若每种信号各写一个替换步骤,处理顺序就会影响结果:先全部转小写,再识别驼峰,已经无法知道原来的单词在哪里分开。
更清楚的路线是先把输入拆成单词序列,再选择目标拼接方式。输入规范化与输出格式化分开后,同一组词可以生成下划线、短横线或首字母大写形式,而不用维护多组互相复制的转换。
缩写边界,是常见的反直觉情况
import re
name = "HTTPServerID"
words = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", name)
words = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", words)
result = words.lower()
assert result == "http_server_id"连续大写字母不一定要逐字拆开。HTTPServer 更合理的分法通常是 HTTP 与 Server,而不是 H、T、T、P、Server。上面先处理“大写串接普通单词”的交界,再处理“小写或数字接大写”的交界。
这是一组明确的示例规则,不是所有自然语言的通用分词器。涉及 Unicode 大小写、多字符大小写映射或语言特定规则时,需要另行定义接受范围,不能把 ASCII 范围的判断直接推广到所有文字。
分隔符清理应该产生稳定结果
处理过程可以先识别大小写变化和分隔位置,再根据目标格式组织结果。需要增加分隔符时,提前评估结果容量,避免把字符替换与缓冲空间变化混在一起。
转换规则还应保持范围明确。面向英文标识符的处理,不必宣称能够自动理解所有语言的单词;遇到超出约定的内容,应由上层决定接受、拒绝或原样保留。
import re
source = " user--profile__name "
parts = [part for part in re.split(r"[-_\s]+", source.strip()) if part]
snake = "_".join(parts).lower()
kebab = "-".join(parts).lower()
assert snake == "user_profile_name"
assert kebab == "user-profile-name"重复分隔符是否折叠,首尾分隔符是否删除,需要在格式中说明。如果输入本来是文件路径或业务编码,连续符号也可能有含义,不应直接复用标识符规范化逻辑。
输出如果要作为程序标识,还需要检查首字符、允许字符集合和保留字。转换风格只是生成候选名称,并不自动保证候选在目标语言中合法。
规范化可能制造碰撞,不能代替唯一标识
user-name、user_name 与 UserName 在某套规则下可能都变成 user_name。对于界面字段名,这种合并也许符合预期;对于数据库键或公开资源地址,就需要检测冲突,不能让后一条静默覆盖前一条。
| 阶段 | 问题 |
|---|---|
| 分词 | 缩写、数字与分隔符怎么形成词段 |
| 拼接 | 目标风格采用什么大小写规则 |
| 合法性 | 能否用于目标语言或地址 |
| 唯一性 | 是否与其他名称归并到同一结果 |
遇到冲突时,追加稳定的业务标识通常比追加随机字符更容易追踪。但这种后缀规则属于命名系统的决策,不应藏在一个基础的风格转换工具里。
用幂等性检查转换规则是否稳定
两个原始名称可能在规范化后变成相同结果。因此,命名转换负责稳定表达,唯一性检查仍由使用结果的配置或数据层承担。
可以用连续大写、数字混排、多重分隔符和首尾空白组成样例集合。对已有系统迁移时,先比较转换前后的键集合,再修改存储或接口映射,避免把格式整理变成无声的数据覆盖。
如果一个名称已经符合目标格式,再转换一次通常应该得到同样的结果。这个性质称为幂等,可以很快发现重复分隔符处理、首尾清理和大小写规则之间的冲突。
还应保留缩写开头、缩写结尾、连续数字、只有分隔符以及空输入等样例。空结果是否允许必须明确,否则上层可能用空字符串创建字段或生成一个无法区分的资源地址。
不要把可逆当成默认承诺。规范化经常主动合并大小写或符号差异,从输出通常无法还原原来的输入。如果界面需要展示用户原名,应该保留原文,规范化结果只用于明确的机器处理用途。