杂记 › 技术笔记 › \u4e2d是什么鬼?中文与Unicode编码在线互转

\u4e2d是什么鬼?中文与Unicode编码在线互转

接手那个跑了七年的老系统时,我要改的只是一句提示文案:在"当前库存不足"后面加个括号说明。文件是 message_zh.properties,1846 行、326 个 key,满屏都是 \uXXXX,搜"库存"两个字一个都命中不了。我先花二十分钟逐行比对定位到那一行,改的时候又手滑把 \u5de5 少打了一位,编译直接报 illegal escape character。绕这一圈才弄清:\u 开头那些不是乱码,是同一批汉字换了一种写法。

一、码点是编号,\uXXXX 只是它的十六进制写法

Unicode 给每个字符分配一个编号,叫码点,与字节、与用什么语言都无关。\u4e2d 的意思就是"这个字符的码点,十六进制写作 4E2D"。同一个"中"字,可以有好几种长相。

汉字码点Unicode 转义HTML 数字实体码点十进制
中U+4E2D\u4e2d中20013
文U+6587\u6587有25991
工U+5DE5\u5de5巧24039
具U+5177\u5177具20855

范围上也值得记两个数:基本多文种平面是 U+0000 到 U+FFFF,一共 65536 个码位,常用符号和汉字大多落在里面;汉字主区段 U+4E00 到 U+9FFF,占 20992 个码位。超出 U+FFFF 的生僻字和 emoji 没法用单个四位转义表示,得用代理对: 是 U+1F600,要写成 \ud83d\ude00 两段。

二、五种"看着都像乱码"的东西,不在同一层

把这几层混起来处理,是损坏数据的最快方式。

长相实际是什么属于哪一层
\u4e2d\u6587字符码点的转义写法源码 / JSON 字符串层
中同一个码点,十进制实体HTML 标记层
%E4%B8%AD"中"的 UTF-8 三个字节 E4 B8 AD 被百分号化URL 传输层
D6 D0"中"在 GBK 里的两个字节字节编码层
中文UTF-8 字节被当成 Latin-1 显示已经错了,是显示事故

最后那行是真乱码:字节解释错了,没有任何"还原成中文"的按钮能把它救回来,只能回到源头改解码方式。同理,"锟斤拷"是把替换字符 EF BF BD 再用 GBK 读了一遍的结果。我见过有人拿 decodeURIComponent 去解 %u4E2D,只换来一句 URI malformed——那是早年 escape() 留下的四不像写法,两头都不算。

三、我最后是怎么改那一行的

  1. 定位。把整个文件的内容用中文Unicode互转还原一版纯中文,在这份还原结果里按"库存"搜索,拿到 key 名,再回到原文件改那一行。原文件一个字符不动,避免整篇往返转换带来看不见的差异。
  2. 确认形式。properties 这种文件按行读,转义符写单反斜杠 \u5de5;如果这段文字要嵌进 Java 源码字符串,就得写成 \\u5de5。少一位或多一位反斜杠,编译器只会甩一句非法转义。
  3. 生成回去的那一行时,我用"仅转非 ASCII"这一档,英文字母、数字和半角标点原样保留,diff 里只会显示我真正改动的那几个字;全选转换会把整行都变成转义,评审时根本看不出改了哪里。
  4. 需要输出成页面能直接用的形式时,同一份文本换十进制模式,拿到 中 这种实体写法。

百分号那一层我用的是别的东西:URL 编解码处理的是字节序列,%E4%B8%AD 归它管;文本编码转换管全角半角和 ASCII 值。三者不能互相代替,层级不一样。

四、几个可以直接抄走的数字

  • 同样一段"中文":2 个字符,UTF-8 下 6 个字节,GBK 下 4 个字节,写成 \u4e2d\u6587 是 12 个 ASCII 字符——一个汉字的转义固定占 6 个字符。
  • 按字符数算额度的地方最吃亏:签名、短信模板、配置项全转义等于涨 6 倍,一旦在中间截断,留下 \u4e 这种半截就是语法错误。
  • 看不见的那几个字符:\u3000 是全角空格(12288),\u00a0 是不换行空格(160),\u201c / \u201d 是中文弯引号(8220 / 8221)。从文档或聊天窗口复制过的文案十有八九带着它们,先还原成中文再查找替换才有意义。
  • 反斜杠的层数别记错:properties 里是单反斜杠 \u5de5,进 Java 源码字符串要写 \\u5de5,这段文本再进 JSON 时又得多剥一层。每跨一层就多一次转义,出错多半是在中间某层少写了。

五、常见疑问

Q:\u4e2d 里的 4e2d 是什么?

是"中"这个字符的 Unicode 码点,十六进制 4E2D,换成十进制就是 20013,所以 HTML 实体里写作 中。两者指向同一个字符,只是十六进制和十进制的区别。

Q:源码里写转义和直接写中文,运行起来有区别吗?

解析之后完全等价,是同一个字符。区别在人这一侧:转义写法让全文搜索、代码评审和对文案都失效,而且要求文件本身只能安全地容纳 ASCII。

Q:为什么以前 Java 的配置文件非要转义?

PropertyResourceBundle 在 JDK 9 之前固定按 ISO-8859-1 读这类文件,中文必须以 \uXXXX 形式存进去,早年 JDK 还专门配了 native2ascii 这个转换工具(后来废弃)。这解释了我手上那份文件为什么满是转义——它是历史约束留下的写法,不是坏数据。

Q:文案里的 emoji 为什么字数总对不上?

因为超出 BMP 的字符要占两个 UTF-16 代码单元:'\ud83d\ude00'.length 是 2,一个笑脸在按长度校验的代码里被算成两个字。要按码点统计就用 codePointAt 一类写法,源码里也可以写 \u{1f600}(ES6 起支持)。至于 Invalid Unicode escape sequence 那个报错,通常来自被截断的转义,比如只剩 \u4e,凑不满四位十六进制。

Q:几百行一起转会不会改坏?

转换本身是无损的,风险在覆盖原件。我的做法是转出来的那份只用来查、不用来存,实际改动落在原文件的那几行,提交前用 diff 确认行数还是 1846 行、只有目标 key 变了。

那句"当前库存不足(可在设置里调整)"最后加了进去,用的是原来的单反斜杠转义写法,编译过了。回头看,那二十分钟里真正值钱的经验不是"找到了转换工具",而是先分清自己面对的是哪一层:转义和实体是同一个码点的两种写法,还原无损;百分号那层归 URL 管;至于 中文 这种字节已经解错的,只能回到读写两端改解码方式,任何"转成中文"都救不回来。

📎 相关文章

大小写、驼峰、下划线命名混乱?批量互转的方法 2026-10-15 这个IP是哪里的?归属地和运营商怎么查、能查到什么 2026-10-15 压缩成一行的JS看不懂?在线美化、格式化代码 2026-10-15 二进制、十六进制总是算不对?2/8/10/16进制互转方法 2026-10-15 接口返回的JSON怎么变成表格?在线转Excel/CSV一步到位 2026-10-15