字母转大写

粘贴一段文本,一键切到全大写、全小写或逐词首字母大写。转换走 Unicode 完整大小写映射,所以德文 ß 会展开成 SS、码点数会变;语言环境可切到土耳其语,让 i 的大写成 İ。

行数2
输入码点数49
输出码点数51
码点变化+2
字符码位toUpperCase()toLocaleUpperCase('tr')码点变化
aU+0061AA1→1
éU+00E9ÉÉ1→1
ßU+00DFSSSS1→2
ıU+0131II1→1
İU+0130İİ1→1
ẞU+1E9Eẞẞ1→1
fiU+FB01FIFI1→2
fflU+FB04FFLFFL1→3
ʼnU+0149ʼNʼN1→2
ǰU+01F0J̌J̌1→2
ẖU+1E96H̱H̱1→2
ΐU+0390ΐ̈́ΐ̈́1→3

提示:文本框里单按 Enter 是换行,按 Ctrl + Enter(macOS 为 Cmd + Enter)立即重算;换节拍或 locale 也会立刻重算。对照表共 16 行,默认显示 12 行。

使用方法

  1. 把要转换的文本粘进文本框,上限 20 万码元。页面打开时框里已经预置两行示例:第一行 straße fußball café résumé,第二行 istanbul ısparta izmir,共 2 行、49 个码点、7 个词。
  2. 选「转换节拍」。全大写调用 toUpperCase(),全小写调用 toLowerCase(),首字母大写按空白切词后把每个词的首字母转大写、其余字母转小写(连空格原样保留)。
  3. 选「语言环境 locale」。默认的 Unicode 映射下示例第二行是 ISTANBUL ISPARTA IZMIR;切到 tr 会变成 İSTANBUL ISPARTA İZMİR,因为土耳其语里 i 的大写是 İ(U+0130),不是 I(U+0049)。
  4. 按需勾选「转换前先做 NFC 归一化」(默认勾选)。从 macOS Finder 或某些编辑器复制的文本常用分解形式,é 会存成 e 加 U+0301 两个码位,不归一直接大写得到 E 加 U+0301;归一后是一个码位 U+00C9。
  5. 点「转换大小写」,或在文本框里按 Ctrl + Enter 重算。结果上方四张卡给出行数、输入码点数、输出码点数与码点变化——示例在全大写下输出 51 个码点,比输入的 49 多 2,来源是两个 ß 各自展开成 SS。

这个字母转大写工具能解决什么问题

一是批量统一常量、枚举与缩写。SQL 关键字、配置项键名、错误码前缀常常要求全大写,一段一段手改容易漏;这里一次转换,并且能立刻从「码点变化」卡片看出有没有 ß 这类会变长的字符混进来。

二是在付印或入库前查出长度会变的行。德文地址里 Straße 转成 STRASSE 会多出一个字符,如果下游字段按原长度截断就会被切掉尾巴。本页把 +2 这种变化直接显示在结果上方,不用自己去数。

三是处理土耳其语这种带点 i 的语言。默认映射下 i 大写是 I,而土耳其语要求 İ;反过来 I 转小写是 ı(U+0131)。做跨国站点的数据清洗时,这个差别会让字符串匹配直接失败,切到 tr 就能看到正确结果。

四是把大小写不敏感的比较做对。比较前应该先统一成同一档;但要注意 ß 大写变 SS、ẞ 小写变 ß 都不是等长替换,靠大写来对齐长度会出错,比较请用小写档。

本页不做这些事:不做标题式大小写(把 a、an、the、of 这类小词留在小写,那是英文标题大小写转换的活);不生成驼峰、帕斯卡、蛇形命名(见驼峰大小写转换器);不做拼写检查与词形还原;也不判断某个词是不是专有名词。

原理与规则说明

这个字母转大写工具的核心只有三个原生方法:String.prototype.toUpperCase()、String.prototype.toLowerCase() 和带参数的 toLocaleUpperCase(locale),外加可选的 String.prototype.normalize('NFC')。按 ECMA-262 的规定,toUpperCase() 必须按 Unicode 的默认大小写映射逐码位替换,而且允许结果与输入长度不同——它不是 ASCII 时代那种减 32 的字节运算。

为什么不默认传 locale。不传参数时走的是 Unicode 默认映射,与语言无关,在任何浏览器、任何版本上结果都一样,可以被测试锁死;传 'tr' 之后结果依赖运行环境的 ICU 数据版本,同一段文本在不同浏览器上可能出现细微差别。所以本页把 locale 做成显式选项:只在明确要处理土耳其语(或立陶宛语)时才切,常规英文、德文、法文一律用默认档。实测 i 在默认档大写为 U+0049,在 tr 档大写为 U+0130;I 在 tr 档小写为 U+0131。

遵循的两份 Unicode 数据。一对一的部分来自 UnicodeData.txt 的简单大小写映射(simple case mapping),一对多的部分来自 SpecialCasing.txt 的完整大小写映射(full case mapping)。ß 之所以变成两个字母 SS,就是 SpecialCasing.txt 里的无条件映射条目,不是工具的自由发挥。ASCII 段依然规整:a(U+0061)与 A(U+0041)相差 32,26 个小写字母全部会变。

输入到输出的中间量。文本先按 \r\n|\r|\n 拆成行数组;全大写、全小写直接对整行调用对应方法;首字母大写则按 (\s+) 切词并保留空白片段(所以连续空格与制表符原样保留),逐词把首字符转大写、剩余部分转小写,最后用 \n 拼回。整条链路不触碰换行,输入几行输出就是几行。

阈值与规模(均为实测,非估算)。文本上限 20 万 UTF-16 码元,超出直接提示分批,不做截断。对 BMP 全部 63488 个非代理码位扫描:toUpperCase() 后与自身不同的有 1273 个,其中长度大于 1 的一对多映射有 102 个。示例文本 49 个码点、43 个拉丁字母,全大写后 51 个码点,差值为 2,正好等于两个 ß 的贡献。

最常见的误用是把「转大写」当成可逆操作。ẞ(U+1E9E)转小写得到 ß(U+00DF),再把 ß 转大写得到 SS——回不到原来的 ẞ。同理 İ 转小写会散成 i 加 U+0307 两个码点。要保留原始写法,就只转一次并保存原始文本,不要来回倒。

完整示例(数值均为实测)

示例是页面上的两行文本:straße fußball café résumé 与 istanbul ısparta izmir,输入 49 个码点。下表是三种节拍与两种 locale 组合后的实测输出与码点数,可以直接和结果区对照。

节拍 / locale输出(两行)码点数
全大写 / 默认STRASSE FUSSBALL CAFÉ RÉSUMÉ
ISTANBUL ISPARTA IZMIR
51(+2)
全大写 / trSTRASSE FUSSBALL CAFÉ RÉSUMÉ
İSTANBUL ISPARTA İZMİR
51(+2)
全小写 / 默认straße fußball café résumé
istanbul ısparta izmir
49(0)
首字母大写 / 默认Straße Fußball Café Résumé
Istanbul Isparta Izmir
49(0)
首字母大写 / trStraße Fußball Café Résumé
İstanbul Isparta İzmir
49(0)

只有「全大写」这一档会变长:straße 变 STRASSE、fußball 变 FUSSBALL,各贡献 +1,合计 +2。其余节拍对这段输入不改变码点总数。切到 tr 之后变化的只是第二行的三个 i:istanbul 变成 İSTANBUL、izmir 变成 İZMİR,码点数仍是一个对一个,总数不变。

输入范围与对照表

输入范围与处理边界:

  • 长度上限 20 万 UTF-16 码元;示例的 49 个码点只占上限的 0.0245%。超限时给出提示并停止转换,不截断已有内容。
  • 按行处理,行数不变。换行是行分隔符,不会变成空格;空行保留。
  • 空白原样保留。与「按空格压成一个」的排版型工具不同,本页只替换字母的码位,多个连续空格、制表符都不动。
  • 中文、数字、标点不参与转换。汉字没有大小写,原样输出;只有夹杂的拉丁字母、希腊字母会被替换。
  • NFC 归一化只合成、不拆解。它把 e 加 U+0301 合成 U+00E9,不会把已经预组合的字符拆开。

不同输入元素走的是不同映射通道,下面这张表是处理口径的实测对照:

输入元素处理方式码点是否变化实测示例
拉丁小写字母按简单大小写映射逐个替换不变(26 个字母全变)a → A,码点差 32
德文 ß按完整大小写映射展开+1straße → STRASSE
预组合 é一对一替换不变café → CAFÉ
分解形式 e + U+0301勾选 NFC 后先合成再替换减少 1(勾选时)café 由 5 码位降到 4 码位
汉字与中文标点不在映射表内,原样保留不变字母转大写 → 字母转大写
换行与连续空格按行处理,空白原样保留不变2 行输入仍是 2 行
数字与半角标点无大小写映射,原样输出不变2026 → 2026

结果边界与 Unicode 映射的坑

  • 长度会变,且只在大写方向变。示例全大写 +2,因为 ß 展开成 SS;反过来 SS 转小写仍是 ss,不会自动变回 ß。小写方向也会变长的例子是 İ(1→2)。
  • 102 个码位是一对多映射。对 BMP 全部 63488 个非代理码位扫描,大写后长度大于 1 的有 102 个,大写后与自身不同的共 1273 个。如果你的文本里有希腊语带调符字符,ΐ 这种会一次多出两个码点。
  • 大写不保证可逆。ẞ → ß → SS,回不到 ẞ。需要保留原文就别来回转换。
  • 不要拿大写结果做长度对齐或截断。下游字段若按输入长度截取,Straße 变 STRASSE 后会被切尾;先转换再算长度。
  • locale 只在你明确需要时切换。切到 tr 后,英文里正常的 i 也会变成 İ,混排文本会因此出现意料外的字符。默认档对英文、德文、法文都是正确且可复现的。
  • NFC 归一化改变的是码点数而不是字形。勾选后 café 从 5 个码位(分解形式)变成 4 个,显示效果相同,但字符串长度、索引与正则匹配都会随之改变。
  • 中文不受影响。汉字不在大小写映射表里,代码块里的中文注释、标点全部原样保留。

常见问题

为什么转换后字符数变多了?是不是出错了?

没有出错。ß(U+00DF)按 Unicode 完整大写映射要展开成 SS,一个字符变成两个,这是 SpecialCasing.txt 的规定。示例里有两个 ß,所以全大写后从 49 个码点变成 51 个,结果区上方的「码点变化」卡片显示 +2。

locale 选 tr 到底改了什么?什么场景才需要?

只改带点的 i 这一组:默认档 i 大写是 I(U+0049),tr 档是 İ(U+0130);I 转小写默认档是 i,tr 档是 ı(U+0131)。处理土耳其语人名、地名、站点数据时才需要;英文文本切过去反而会得到 İSTANBUL 这种混排结果。

「转换前先做 NFC 归一化」什么时候必须取消勾选?

当你在检查文本的归一化状态时不要勾:勾上会把分解形式合成预组合形式,文本本身被改写,你就看不出原始输入是不是 NFD 了。正常做转换、要入库或要交给下游系统时保持勾选,能保证 é 是一个码位而不是两个。

「首字母大写」和英文标题大小写转换有什么不一样?

本页的首字母大写是逐词机械处理:每个词首字母转大写、其余转小写,不认识小词,所以 the quick brown fox 会得到 The Quick Brown Fox。标题式会把 a、an、the、of、in、on、and、or、to 这类小词留在小写,那是英文标题大小写转换的规则。

能把大写后的文本还原成原来的大小写吗?

大多数字符可以,但有几个不可逆:ẞ 转小写是 ß,再转大写得到 SS 而不是 ẞ;İ 转小写散成 i 加 U+0307。大小写信息在转换时就被丢弃了,需要保留原文请另存一份。

为什么 İ 转小写后字符数从 1 变成 2?

因为 İ(U+0130)的小写映射是 i 加上组合用上点 U+0307,共两个码位。这是 Unicode 里少数「小写方向变长」的条目,和 ß 在大写方向变长是同一类现象。

输入上限是多少,超了会怎样?

上限 20 万 UTF-16 码元。超限时结果区清空并在错误区提示当前码元数与 20 万上限,让你分批转换,不会静默截断——截断会让你误以为文本已经全部处理完。

我的文本会被上传到服务器吗?中文内容会受影响吗?

不会上传,转换全部由浏览器原生字符串方法完成,不发任何网络请求,可以在开发者工具的 Network 面板自查。中文字符没有大小写映射,原样输出;只有拉丁、希腊等有大小写之分的字母会被替换。

相关工具