文本转十六进制转换器

把文本按 UTF-8 编成字节,再逐字节转成十六进制。页面用 TextEncoder 出结果,并把字符数、UTF-16 长度、UTF-8 字节数、hex 长度、膨胀倍数、字节与字符之比分开列——中文为什么是 6 个 hex 字符、emoji 为什么是 8 个,都在这几个数字的差额里。

E4B8ADE69687616263313334

编码链路:文本 → new TextEncoder().encode(s) → Uint8Array → 每字节 toString(16).padStart(2,'0')。单字节一定要补到两位,否则长度会塌陷。

使用方法

  1. 打开页面,输入框已预填 中文abc134,结果区直接显示 E4B8ADE69687616263313334——这是 12 字节编出的 24 个 hex 字符。改动输入内容会立刻重算,不用点按钮。
  2. 多行输入按 Ctrl/Cmd + Enter 触发一次完整重算。这里用组合键而不是裸 Enter,是因为输入框是多行文本,Enter 必须留给换行——而换行符本身就是待编码的数据,它的 hex 是 0a。
  3. 选择分隔符:无 / 空格 / 每 8 字节换行 / 每 16 字节换行。后两种是为了对齐 hex dump 的常规排版。分隔符只影响显示,不改动任何一个字节,四种格式里 hex 长度(不含分隔)始终相同。
  4. 切换大小写、勾选 0x 前缀。它们同样只是显示约定:小写 e4b8ad 与大写 E4B8AD 数值相同,加前缀后每字节多 2 个字符,字节数不变。页面默认输出大写,与反向解码页的默认输入形态保持一致;正文对照表按 RFC 习惯统一用小写呈现。
  5. 看结果区下方六个指标:字符数(按码点)、UTF-16 长度、UTF-8 字节数、hex 长度(不含分隔)、膨胀倍数、字节与字符之比。默认示例是 8 码点 / 8 个 UTF-16 码元 / 12 字节,12 与 8 的差额只有一个来源:两个汉字各占 3 字节。

文本转十六进制转换器能解决什么问题

先把口径说清楚:本页输出的是文本按 UTF-8 编码后的字节的十六进制,不是「字符的 Unicode 码点」。这两者经常被混为一谈,数值却完全不同——「中」的码点是 U+4E2D,而它的 UTF-8 字节是 E4 B8 AD。文本转十六进制转换器算的是后者。同样叫「转十六进制」,数值口径与字节口径是两件事:把十进制数 255 变成 FF 属于数值进制转换,把「中文」变成 E4B8ADE69687 属于文本编码,两者只在 ASCII 区间才碰巧一致。

一是给接口 / 协议 / 硬件准备字节。串口指令、蓝牙特征值、部分 HTTP 签名、嵌入式寄存器,要求的是一串十六进制字节而不是字符串。要在 payload 里塞一个「中文」,你就必须知道它是 E4 B8 AD、长度为 3,而不是想当然的一位汉字。本页把字节数与 hex 长度分开列,正是为了让「3 字节 → 6 个 hex 字符」这个换算随手可查。

二是核对编码到底对不对。看到乱码,第一步往往是把原文转成 hex,看字节落在哪一段区间:全在 00~7F 是 ASCII;出现 C2~DF 说明混进了 2 字节字符;E0~EF 是 3 字节(绝大多数汉字都在这一档);F0~F4 是 4 字节(emoji 与增补平面)。把「猜编码」变成「看字节」,是排查乱码最省时间的一步。

三是造测试数据与转义文本。写单测时要构造含中文、emoji、控制字符的字节串;把参数塞进 URL、Cookie、配置文件时,也常要求先拿到字节再做百分号编码或 \xNN 转义。本页给出的就是这一层最原始的字节。

四是与 JSON 的 \u 转义互校。JSON 用的是 UTF-16 码元转义,和本页的 UTF-8 字节口径并不是一套算法:「中」在两边都恰好是 6 个字符(\u4e2d 对 e4b8ad),是个巧合;但 😀 在 JSON 里是 12 个字符(\ud83d\ude00),在本页只有 8 个(f09f9880)。把两套口径搞混,是「转义后长度对不上」最常见的成因。

本页不做这几件事:不做 GBK / Big5 编码——浏览器原生 TextEncoder 只输出 UTF-8,别的编码得靠专门的字库;不做数值进制转换——那是十进制 / 二进制 / 十六进制互转;也不做反向的「十六进制转文本」,那是本页的镜像页,两页互为反方向、可交叉验证。

原理和变量:字符 → 码点 → UTF-8 字节 → hex 的四段转换

编码链路一共四段:JS 字符串(UTF-16 码元)→ Unicode 码点 → UTF-8 字节 → 每字节 2 位十六进制。new TextEncoder().encode(s) 一步走完前两段,直接返回一个 Uint8Array;剩下的转换是纯机械操作。文本转十六进制转换器的全部工作,就是保证这条链上每一环都不出错。理解这个链条的关键是记住UTF-16 和 UTF-8 是两套长度不同的编码,中间必须经过码点这一层才能对齐。

第一段:码点决定 UTF-8 字节数。RFC 3629 把码点区间与字节数一一对应,没有例外:

第二段:单字节必须补零,这是最容易写出静默 bug 的地方。一个字节 8 位对应 2 位十六进制,所以每个字节都必须输出满 2 个字符。0x05 写成 "5" 就错了。实测反例:字节 [0x05, 0x5A, 0xFF] 正确结果是 055aff(6 个字符),若省略补零会得到 55aff(5 个字符)——长度不再是「字节数 × 2」的偶数,反向解析时必然错位、整串作废。页面统一用 t.length < 2 ? '0' + t : t 强制补足两位。

第三段:JS 字符串按 UTF-16 码元存放,U+10000 以上必须用代理对。所以一个 emoji 的 s.length 是 2 而不是 1,而 [...s].length(按码点迭代)才是 1。落单的代理项——只有高代理 \uD83D 或只有低代理 \uDE00——不是一个合法字符:UTF-8 规范禁止编码 U+D800~U+DFFF 这段保留区,TextEncoder 会把它静默替换成 U+FFFD 的字节 EF BF BD(3 字节)。替换之后再解码回文本就是一个 �,原文信息永久丢失。本页因此直接拒绝含落单代理项的输入,而不是让你对着 � 猜原文。

第四段:分隔符、大小写、0x 前缀都是显示约定,不进入字节流。它们只在最后一步拼接字符串时生效。可以量化的关系是:hex 字符数(无分隔、无前缀)= 字节数 × 2;改用空格分隔时再加 字节数 − 1 个空格;每字节加 0x 前缀则每字节多 2 个字符。默认示例 12 字节,于是无分隔是 24 字符、空格分隔是 24 + 11 = 35 字符、加前缀是 48 字符——三者的字节序列完全一样。文本转十六进制转换器把这四种格式都做成了可切换项,但底层只算一次字节。

完整示例(数值仅作演示)

示例条件与页面打开时的默认值完全一致:文本 = 中文abc134,输出 = E4B8ADE69687616263313334。结果区在打开时就显示着这串 hex,点一次「转十六进制」得到完全一样的结果。文本转十六进制转换器的默认值之所以固定成这一组,是为了能直接粘进反向页做交叉验证。

输入范围与对照表

输入侧的规则都对应一种真实的输入形态,而不是凭空约定:

输入形态示例页面行为为什么要这么定
纯 ASCIIA1~`正常编码,1 字符 1 字节最常见场景,hex 长度正好是字符数 × 2
汉字 / 全角标点中文,正常编码,1 字符 3 字节U+0800~U+FFFF 统一 3 字节,hex 长度 6
emoji / 增补平面😀🀄𝄞正常编码,1 码点 4 字节U+10000 以上既要代理对又要 4 字节 UTF-8
控制字符换行 / TAB正常编码,hex 为 0a / 09控制字符也是数据,必须编进字节而不是丢掉
空输入(留空)拒绝空串的 hex 结果同样是空串,无法与「没填」区分
落单代理项截断的 😀拒绝UTF-8 禁止编码 U+D800~U+DFFF,库会静默换成 EF BF BD
超长输入> 200 万码元拒绝(20 万起先提示)逐字节循环加超长 DOM 文本都会把页面卡死

下表是 20 组字符的逐项实测:「UTF-8 字节数」与「hex 字符数」两列始终满足「hex = 字节 × 2」。前 12 行默认显示,点「加载更多行」展开后 8 行;点「导出 CSV」可整表带走(带 BOM,Excel 打开中文不乱码)。表中 hex 统一用小写 0-9a-f 呈现,与页面输出的默认大写只是写法差异,数值完全一致。

行字符类别码点UTF-8 字节数hexhex 字符数
1(默认)AASCII 大写字母U+00411412
2aASCII 小写字母U+00611612
31ASCII 数字U+00311312
4~ASCII 标点U+007E17e2
5é拉丁扩展 2 字节U+00E92c3a94
6Ω希腊字母 2 字节U+03A92cea94
7中CJK 汉字 3 字节U+4E2D3e4b8ad6
8,全角标点 3 字节U+FF0C3efbc8c6
9€欧元符号 3 字节U+20AC3e282ac6
10😀emoji 增补平面 4 字节U+1F6004f09f98808
11𝄞音乐符号 4 字节U+1D11E4f09d849e8
12换行 \nLF 控制符 1 字节U+000A10a2

第 5 行值得注意:é 的码点 U+00E9 已经越过 U+007F,于是从 1 字节跳到 2 字节,hex 从 2 个字符变成 4 个。第 10、11、20 行则是 U+10000 以上的样本,UTF-16 里各自占 2 个码元、UTF-8 里占 4 字节——「1 个字符却要 8 个 hex 字符」的膨胀上限就出现在这一档。第 12、18、19 行提醒一件事:控制字符不是「看不见就可以不管」,它们同样要编成字节,DEL(U+007F)甚至刚好卡在 ASCII 区的最右端。

边界与精度

常见问题

「中」转出来是 6 个字符,可它明明只是一个汉字?

因为 hex 是字节的十六进制写法,不是字符的写法。一个 UTF-8 汉字占 3 字节,每字节 2 位十六进制,所以「中」是 E4 B8 AD——6 个字符。对照表第 7 行就是这一条:码点 U+4E2D 落在 U+0800~U+FFFF 区间,输出恰好 3 字节、6 个 hex 字符。
反过来理解更直观:字符数不是你该看的东西,字节数才是。页面的指标区把「字符数(按码点)」和「UTF-8 字节数」并排放在一起,默认示例就是 8 对 12。如果你要数的是「几字节」,看字节数那一格;如果要的是「几个字」,看字符数那一格。把「8 个汉字变成 48 个字符」当成转错了,是这类工具最高频的误判。

换个分隔符,结果看起来完全不一样,是我切换错了?

不是。分隔符只改排版,不碰字节。同一串 12 字节在四种格式下的「hex 长度(不含分隔)」都是 24,区别只在中间插了什么:无分隔是 e4b8ade69687…;空格分隔每字节之间加一个空格,总共加 11 个;每 8 字节换行则是把 12 个字节分成 2 行。验证办法很简单:把任意一种格式粘回去、去掉所有空格与换行,得到的字符串应当与「无分隔」一模一样。页面里空格分隔的长度 35 = 24 + (12 − 1),这个式子就是在说明「多出来的字符全是分隔符」。
顺带说一句选择建议:给程序解析用「无」或「空格」;要和 xxd、Wireshark 那种 16 字节一行的 dump 对照用「每 16 字节换行」;要嵌进源码常量用带 0x 前缀的版本。

emoji 明明是 1 个字符,为什么转出来 8 个 hex 字符?

因为 emoji 的码点在 U+10000 以上,属于「增补平面」,UTF-8 必须用 4 字节编码它,于是 hex 是 8 个字符(F0 9F 98 80),这是本页的膨胀上限。同时别忘了第二套长度:JS 字符串按 UTF-16 存放,同一个 emoji 要用代理对拆成 2 个码元,所以 '😀'.length 是 2,而 [...'😀'].length(按码点)才是 1。
于是同一个字符在三个地方有四个数字:码点 1 个、UTF-16 码元 2 个、UTF-8 字节 4 个、hex 字符 8 个。页面的指标区把这几个数分别列出,就是为了让你不再把它们混着用。
更极端的例子是组合 emoji:👨‍👩‍👧 由 3 个人物加 2 个 U+200D 零宽连接符组成,实测 5 个码点、8 个 UTF-16 码元、18 个 UTF-8 字节,hex 长达 36 个字符——它在视觉上是「一个」表情,在字节层面却是一个 18 字节的序列,这正是「字符」这个词在编码语境下必须被拆开的原因。

从记事本复制多行文本,行尾字节怎么只有 0a?

因为本页在浏览器里读的是 textarea 的值,而 HTML 规范要求浏览器把换行统一规范化为 LF。所以无论你从 Windows 记事本(原本是 CRLF)、macOS、还是 Linux 复制进来,取到的都只是 0a。页面的对照表第 12 行把这个值明明白白写成了 0a。
如果你要生成的字节流必须匹配目标平台,就得自己补:Windows 的 CRLF 是 0d0a,要在行尾再手工加一个 CR;老式 Mac 的 CR 是 0d。另一个坑是隐藏字符:从网页复制的文本常夹带零宽空格 U+200B(3 字节 e2808b)或不换行空格 U+00A0(2 字节 c2a0),它们肉眼看不见却会实打实出现在 hex 里。看到长度比自己数的多出几字节,优先怀疑这类字符。

默认输出是大写,结果要小写怎么办?

直接切「大小写」为小写 a-f 即可,字节序列不变。大小写只是显示约定:E4B8AD 与 e4b8ad 是同一串字节的两种写法,十六进制本身大小写不敏感。页面默认大写,是为了和反向解码页的默认输入形态对齐、方便两页互相粘贴验证;RFC 风格建议统一用小写 0-9a-f,正文里的字符对照表就按小写给出。
需要提醒的是:大小写只在「显示」层面无所谓,在「比较」层面必须统一。如果你要把本页结果和另一段小写 hex 做字符串比对,先各自归一化大小写,否则 E4B8AD 与 e4b8ad 会判为不相等——这是「明明一样却比对失败」的常见原因。页面的「复制结果」复制的是当前显示形态,所以你选了哪种大小写,粘出去就是哪种。

为什么单个字节一定要补零?不补会怎样?

因为一个字节 = 8 位 = 2 位十六进制,每个字节必须占满 2 个字符,否则整串的长度关系就断了。实测反例:字节 [0x05, 0x5A, 0xFF] 正确输出是 055aff(6 个字符),如果不补零,首字节 0x05 会变成 "5",结果成了 55aff——只有 5 个字符。
后果不是「难看」而是「解析错位」:反解程序按每 2 个字符切一刀,55aff 会把 55、af 当成两个字节,剩下的 f 落单,轻则整个解析失败,重则悄悄解出一串错误字节。凡是出现值小于 0x10 的字节(也就是 hex 只有 1 位),都必须补零。本页统一用 t.length < 2 ? '0' + t : t 处理,所以像「中」的第 7 行、换行的第 12 行都不会出问题。

我输入里有一个被截断的 emoji,为什么不让转?

因为落单代理项无法被编码成合法 UTF-8。emoji 在 JS 字符串里是一对代理码元(高代理 U+D83D + 低代理 U+DE00 这类),如果你只拿到其中一半——比如复制时被截断、或者后端按字符截取时切在了中间——剩下的那半个就是U+D800~U+DFFF 保留区里的孤立值。UTF-8 规范明确禁止编码代理项,TextEncoder 的处理方式是不报错、悄悄替换成 U+FFFD,输出 EF BF BD 三个字节。
这很危险:替换后的字节看起来「合法」,你完全不知道原文被破坏了;而且再解码回来只剩一个 �,原文是什么已经永久丢失,任何工具都还原不了。所以本页选择直接拒绝,并在提示里告诉你有几个落单代理项、它们会被替换成什么。要恢复就先找到源头把字符补全,或者干脆删掉这半个码元——不要指望编码器替你猜。

我用 JSON.stringify 得到的是 \u4e2d,和这里的 e4b8ad 不一样,该用哪个?

两套口径,用途不同,不存在「哪个对」。JSON 的 \uXXXX 转义按 UTF-16 码元来:BMP 内的字符一个转义(「中」→ \u4e2d,6 个字符),增补平面的字符要拆成两个转义(😀 → \ud83d\ude00,12 个字符)。本页的 hex 按 UTF-8 字节来:每个字节 2 位,汉字 6 个字符、emoji 8 个字符。
实测对照很能说明问题:中 两边都恰好 6 个字符,是个巧合;😀 在 JSON 里 12 个、在本页 8 个;👨‍👩‍👧 在 JSON 里 48 个、在本页 36 个。而且两者不能互相解析——把 f09f9880 当成 \u 转义去读会失败,因为它不是 4 位的 \u 序列。
怎么选:要写进 .json 文件、嵌进 JS / Java 字符串字面量,用 JSON 那种转义(这也正是「Unicode转义反转义工具」处理的东西);要让字节真正进入协议、文件、硬件的字节流,用本页的 UTF-8 hex。两者可在「十六进制转文本转换器」与「Unicode转义反转义工具」之间往返核对。

相关工具