在线 Unicode 编码/解码工具:处理 Unicode 字符转换
什么是 Unicode
Unicode 是一个国际标准,为世界上几乎所有书写系统的每个字符分配唯一的编号(码点,Code Point)。它解决了早期编码(ASCII、GB2312、Shift-JIS)的兼容性问题——用在线 unicode encoder/decoder 可以在这些表示形式之间转换,如果需要二进制到文本的编码则用 base64 decoder。
Unicode 表示形式
| 形式 | 示例 | 说明 |
|---|---|---|
| Unicode 码点 | U+4F60 | 标准表示法 |
| UTF-8 | \xe4\xbd\xa0 | 变长编码(1-4 字节) |
| UTF-16 | \u4f60 / \uD83D\uDE00 | BMP 两字节;增平面用代理对 |
| Unicode 转义序列 | \u4F60 或 \u{1F600} | JavaScript/Java 中常见 |
| HTML 实体 | 你 | 十进制实体(见 HTML 实体工具) |
| HTML 实体(十六进制) | 你 | 十六进制实体 |
为什么需要 Unicode 编码/解码
- 跨语言兼容:不同编程语言间的字符传输
- JSON 安全:确保 JSON 中的特殊字符正确传输
- 数据库存储:处理数据库的字符编码问题
- URL 编码:URL 中包含非 ASCII 字符时的编码
- 日志分析:处理包含 Unicode 字符的日志数据
- 国际化开发:处理多语言文本的编码转换
UTF-8 vs UTF-16
| 特性 | UTF-8 | UTF-16 |
|---|---|---|
| 字节数 | 1-4 字节 | 2-4 字节 |
| ASCII 兼容 | ✅ 完全兼容 | ❌ 不兼容 |
| 网络传输 | ✅ 主流 | ❌ 不常用 |
| 字节序 | 无(无 BOM) | 有(大端/小端) |
| 空间效率 | 英文高效 | 中文高效 |
如何使用在线工具
使用 ToolVault 工具匣 的 Unicode 编码/解码工具:
- 在左侧粘贴原始文本(可含中文、Emoji)
- 选择编码格式:
\uXXXX:UTF-16 码元转义;增平面字符自动拆成高低代理对(与JSON.stringify习惯一致)\u{XXXXX}:按完整码点转义(ES6 风格,适合文档与现代 JS)U+XXXX:标准码点列表,便于对照规范\xHH:UTF-8 字节序列,便于对照抓包或文件十六进制
- 点「编码」得到转义串;点「解码」可混合识别上述形式(含代理对)
- 顶部同时显示三种长度:UTF-16 码元数、码点数、UTF-8 字节数
- 下方码点明细表列出每个字符的
U+…、UTF-16 转义与 UTF-8 字节
孤立代理项(只有 \uD83D 没有低代理)或非法 UTF-8 字节会被明确报错,而不是静默产出半截字符。
代码中的 Unicode 处理
JavaScript
// 字符 → Unicode 码点(按标量值迭代,勿用 charCodeAt 循环)
function toUnicode(str) {
return Array.from(str).map((char) => {
const cp = char.codePointAt(0);
return 'U+' + cp.toString(16).toUpperCase().padStart(cp > 0xffff ? 5 : 4, '0');
});
}
// Unicode 码点 → 字符
function fromUnicode(codePoint) {
return String.fromCodePoint(parseInt(codePoint.replace(/^U\+/i, ''), 16));
}
// ES6 码点转义(emoji 不要写成非法的 \\u1F600)
function toBracedEscapes(str) {
return Array.from(str)
.map((char) => {
const cp = char.codePointAt(0);
return '\\u{' + cp.toString(16).toUpperCase() + '}';
})
.join('');
}
// JSON 风格:BMP 用 \\uXXXX,增平面拆成代理对
function toSurrogateEscapes(str) {
return Array.from(str)
.map((char) => {
const cp = char.codePointAt(0);
if (cp <= 0xffff) {
return '\\u' + cp.toString(16).toUpperCase().padStart(4, '0');
}
const adj = cp - 0x10000;
const hi = 0xd800 + (adj >> 10);
const lo = 0xdc00 + (adj & 0x3ff);
return (
'\\u' + hi.toString(16).toUpperCase().padStart(4, '0') +
'\\u' + lo.toString(16).toUpperCase().padStart(4, '0')
);
})
.join('');
}
Python
import unicodedata
# 字符 → Unicode 码点
def to_unicode(char):
return f"U+{ord(char):04X}"
# Unicode 码点 → 字符
def from_unicode(code_point):
return chr(int(code_point.replace('U+', ''), 16))
# 获取字符信息
def char_info(char):
return {
'char': char,
'code_point': f"U+{ord(char):04X}",
'name': unicodedata.name(char, 'UNKNOWN'),
'category': unicodedata.category(char)
}
常见 Unicode 字符范围
| 范围 | 内容 | 示例 |
|---|---|---|
| U+0000-U+007F | 基本拉丁字母 | A, b, 1, ! |
| U+0080-U+00FF | 拉丁补充 | á, ñ, ü |
| U+0400-U+04FF | 西里尔字母 | А, Б, В |
| U+4E00-U+9FFF | CJK 统一汉字 | 中, 文, 字 |
| U+3040-U+309F | 平假名 | あ, い, う |
| U+30A0-U+30FF | 片假名 | ア, イ, ウ |
| U+1F600-U+1F64F | Emoji 表情 | 😀, 😂, ❤️ |
三种编码形态与 UTF-8 为什么赢了 web
UTF-8(1-4 字节变长、ASCII 透明)、UTF-16(2 或 4 字节——JavaScript/Java/C# 字符串的内部形态)、UTF-32(定长 4 字节、罕见)。UTF-8 赢 web 因为对 ASCII 后向兼容(英文文本字节相同——90 年代互联网零迁移成本)且自同步(解析器从任意位置找到字符边界——损坏保持局部)。赢家的代价:CJK 每字符 3 字节(UTF-16 只要 2——中文存储贵 50%)、emoji 4 字节。工具含义:字节长、字符长、码点长是三个不同的数——「这字符串多长」必须先说单位;编码器会同时报这三种长度。
NFC 与 NFD:会失败的相等性
同一个视觉字符可以有多套码点序列:é 是预组合字符(U+00E9)或 e 加组合重音符(U+0065 U+0301)。渲染相同、字符串比较不同——「手输能用粘贴不行」的用户名 bug 和数据库重复条目的元凶。规范化到 NFC(web 的组合标准)修复比较;需要它的边界:用户输入对存储值、文件名比较、搜索。更深一层:Unicode 本身在加字符(emoji 版本),最新码点的「规范化相等」依赖版本——长寿系统钉住 Unicode 版本,否则库升级间相等性漂移。
码点、字符、字位簇的鸿沟
人类感知的「一个字」(带肤色的 emoji、韩文音节块、ZWJ 家庭表情)是字位簇——可能许多码点。下面每层都撒一点谎:码点把带修饰的 emoji 数成多个;UTF-16 码元把增平面的劈成两半(用户看到 6 个、字符串长度 11);字节再膨胀一轮。截断、光标移动、列对齐都要字位边界(JS 的 Intl.Segmenter)——数别的层就产出用户截图举报的「半个 emoji」。诚实的工具报长度时说单位;解码器显式展示码点——那是机器看到的,与眼睛看到的差距就是课。
代理对:JS 字符串长度的隐形坑
😀.length 在 JavaScript 里是 2,不是 1:引擎按 UTF-16 码元计数,增平面字符被编码成一对高低代理(如 \uD83D\uDE00)。用 charCodeAt 循环再拼 \uXXXX 时,若只处理一半代理,解码端会得到孤立代理或乱码。可靠做法是 codePointAt / for…of / Array.from,或让工具按标量值编码。反过来:合法 JSON 里常见成对的 \uD83D\uDE00;单独的 \uD83D 则应被拒绝——本站解码器对孤立代理会给出明确错误,而不是悄悄留下半个 emoji。
FAQ
为什么同一个汉字在不同编码中大小不同?
不同编码方式使用不同字节数:UTF-8 中汉字占 3 字节,UTF-16 中占 2 字节,GBK 中占 2 字节。这是编码方案设计的不同。
Unicode 和 UTF-8 有什么关系?
Unicode 是字符集标准(定义每个字符的编号)。UTF-8 是 Unicode 的一种编码实现(定义如何用字节表示这些编号)。UTF-8 是目前互联网最广泛使用的编码。
为什么有些中文字符显示为乱码?
常见原因:编码声明不匹配(文件用 UTF-8 保存但以 GBK 读取)、缺少字体支持、传输过程中编码转换错误。确保全链路使用统一的 UTF-8 编码;当字符显示异常时,可以用 unicode encoder/decoder 检查底层码点。
\uXXXX 和 \u{XXXXX} 该用哪一种?
需要与旧版 JSON / 大量现存转义兼容时,用 \uXXXX(emoji 会变成两个转义)。需要一眼看出真实码点、或写现代 JS 源码时,用 \u{1F600}。本站工具两种都能编、都能解;对照抓包或 hex dump 时再切到 UTF-8 \xHH。
排查注记
「这字符串多长」必须先说单位——字节长、码点长、UTF-16 码元长是三个不同的数,工具顶部同时报这三种长度、明细表逐字列出,正是这个论点的产品化。代理对一节用 😀 的 length 是 2 做锚点:引擎按码元计数,孤立代理在本站解码器里会明确报错、而不是悄悄留下半个 emoji——与码点/字位簇一节的结论呼应:数错层,就会产出用户截图举报的「半个 emoji」。NFC/NFD 一节标题就叫「会失败的相等性」:é 有两套码点序列,渲染相同、字符串比较不同,是「手输能用、粘贴不行」这类用户名 bug 的元凶。
感谢阅读这篇 ToolVault 工具匣 技术文章。更多开发者工具请访问 首页。