二进制转文本

把 8 位一组的二进制串按 UTF-8 规则还原成文字,逐字节标出首字节类型、掩码与替换字符位置。

#8 位串十六进制字节角色掩码取值归属字符

提示:在输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter)即可还原一次——Enter 留给换行,方便按每行一个字节粘贴。默认示例 13 个字节,解出 6 个码点,其中汉字各占 3 字节、😀 占 4 字节。

使用方法

  1. 把 8 位一组的二进制串粘进输入框。页面打开时已经预置示例:11100110 10011111 10110011 11100110 10110011 10001001 00100000 01001111 01001011 11110000 10011111 10011000 10000000,一共 13 组、104 位。
  2. 分隔符随你怎么写:空格、换行、逗号、制表符都会被过滤掉,直接连写成 111001101001111110110011 也一样能识别。唯一的要求是有效 0/1 字符的总数是 8 的整数倍。
  3. 点「开始还原」,结果框给出文本,下面的表格逐字节告诉你每个字节扮演什么角色:ASCII 单字节、2/3/4 字节首字节、还是续字节,以及掩码取值和它最终归属哪个字符。
  4. 碰到非法序列时默认按容错处理:整段解码不中断,非法的那一段换成替换字符 U+FFFD(显示成 �),表格里那一行会标红,位置清清楚楚。想看严格结果就勾上「严格模式」,此时遇到第一个非法序列就直接报错并指出是第几个字节。
  5. 输入框里按 Ctrl + Enter(macOS 用 Cmd + Enter)等同于点「开始还原」。表格默认 12 行,「加载更多」每次再放 12 行;「复制对照表」输出 TSV,「导出 CSV」输出带 BOM 的文件,Excel 打开不乱码。

这个二进制转文本工具能解决什么问题

一是读懂协议与文件头。抓包工具、串口日志、文件十六进制编辑器里,字节常常被展开成 8 位串,想确认一段 11100110 10011111 10110011 到底是哪个汉字,直接贴进来即可。

二是验证编码是否正确。某段文本显示成乱码时,把原始字节贴进来,如果表格里出现大量 U+FFFD,说明字节流本身就不是合法 UTF-8;如果全部正常解出,问题多半出在别处(字体、传输截断或渲染环节)。

三是讲清「一个汉字几个字节」。ASCII 字母 1 字节、常用汉字 3 字节、表情 4 字节,这个分档在表格里一眼能看到,比背规则牢靠。存储容量、字段长度、传输分包都按字节算,用这里的数字做估算是准确的。

四是排查「截断到半个字符」的事故。日志被按定长切片时,末尾常剩半截多字节序列。本页会把这种残尾单独标成一个替换字符,你就能确认是截断导致的乱码,而不是编码选错。

本页不做这些事:不做反向的文本到 8 位串转换(那有专门的一页);不解析 GBK、Big5、Shift_JIS 等非 UTF-8 编码;不做进制计算。要按位做与、或、异或运算请用「二进制计算器」,要看十六进制形态的字节流请用「十六进制转文本转换器」。

还原原理:RFC 3629 的 UTF-8 字节分档与掩码

二进制转文本的核心不是「把 0 和 1 变成字符」,而是先把 8 位串还原成字节,再按 UTF-8 规则把字节还原成字符。前者是纯粹的进制换算(每 8 位一字节,按权相加),后者才是难点:一个字符可能占 1 到 4 个字节,必须靠首字节的高位模式判断它后面还跟几个字节。

RFC 3629 把 UTF-8 的合法字节序列写成 ABNF 文法,分档是这样的:0xxxxxxx 是单字节,覆盖 U+0000–U+007F;110xxxxx 10xxxxxx 是 2 字节,覆盖 U+0080–U+07FF;1110xxxx 10xxxxxx 10xxxxxx 是 3 字节,覆盖 U+0800–U+FFFF;11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 是 4 字节,覆盖 U+10000–U+10FFFF。判断规则就是数首字节开头连续几个 1:0 个是单字节,2 个跟 1 个续字节,3 个跟 2 个,4 个跟 3 个。续字节一律以 10 开头,占 6 个有效位。

掩码是拼接的关键。首字节里真正有效的是低位:2 字节首字节取 & 0x1F(低 5 位),3 字节取 & 0x0F(低 4 位),4 字节取 & 0x07(低 3 位);每个续字节取 & 0x3F(低 6 位)。取出的位依次左移拼接,就得到码点。以实例里的柳为例:三个字节是 E6 9F B3,化成二进制是 11100110 10011111 10110011;首字节取低 4 位得 0110,两个续字节各取低 6 位得 011111 和 110011;拼起来是 0110 0111 1111 0011,也就是十六进制 67F3,十进制 26611,正是 U+67F3。

RFC 3629 比「数前导 1」更严。它同时禁止了三类序列,这也是「非法序列」判定的依据:一是过长编码(overlong),同一个码点用了比必要更长的字节数,例如用 C0 80 表示 U+0000、用 E0 80 80 表示 U+0000,合法写法必须是最短形式;二是代理区码点,U+D800–U+DFFF 这两个区间在 UTF-8 里永远不允许出现,它们的编码是 ED A0 80 到 ED BF BF,统统非法;三是超出 U+10FFFF,所以首字节 F5–FF 以及 F4 90 及以后的序列都不合法。规范写法把 3 字节首字节拆成三段(E0 后面必须跟 A0–BF,ED 后面必须跟 80–9F,其余跟 80–BF),4 字节同理(F0 后跟 90–BF,F4 后跟 80–8F),就是为了把这几类非法形态堵在语法层。

页面里做的是同一件事,并且和浏览器原生实现交叉验证。解析用的是手写状态机:读首字节判长度、逐个校验续字节、用掩码拼码点、再检查是否过长编码或落在代理区;非法时按 WHATWG 编码标准里 TextDecoder 的做法只消费 1 个字节再重新同步,把这一字节记作 U+FFFD。之所以只消费 1 字节而不是整组丢掉,是为了让 C0 80 这种「两个字节各自都不合法」的输入产出两个替换字符,与 new TextDecoder('utf-8').decode() 的输出逐字符一致;勾选严格模式时则改用 new TextDecoder('utf-8',{ fatal: true }) 的行为,遇到第一个非法序列直接抛错。最后用 String.fromCodePoint() 把码点装回 JavaScript 字符串——注意这一步之后,U+10000 以上的字符在内存里就变成 UTF-16 代理对了。

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

示例条件与页面默认值完全一致:输入 13 组 8 位串,共 104 位、13 字节,勾选状态为默认(容错模式)。解析结果如下:

把账算清:汉字 2 个 × 3 字节 + 空格 1 字节 + ASCII 字母 2 字节 + 表情 4 字节 = 13 字节,解出 6 个码点,在 JavaScript 字符串里占 7 个 UTF-16 码元——多的那 1 个码元来自 😀。

把这 13 个字节的角色列成表,就是页面下方那张明细表的静态版本(这里已按字符归组):

8 位串十六进制字节角色掩码取值归属字符
11100110E63 字节首字节1110 & 0x0F = 0110柳
100111119F续字节10 & 0x3F = 011111柳
10110011B3续字节10 & 0x3F = 110011柳
11100110E63 字节首字节1110 & 0x0F = 0110泉
10110011B3续字节10 & 0x3F = 110011泉
1000100189续字节10 & 0x3F = 001001泉
0010000020ASCII 单字节不取掩码(空格)
010011114FASCII 单字节不取掩码O
010010114BASCII 单字节不取掩码K
11110000F04 字节首字节11110 & 0x07 = 000😀
100111119F续字节10 & 0x3F = 011111😀
1001100098续字节10 & 0x3F = 011000😀
1000000080续字节10 & 0x3F = 000000😀

柳 的三个掩码值是 0110、011111、110011,按 4 + 6 + 6 位拼成 0110011111110011,十六进制就是 67F3;😀 的四个掩码值是 000、011111、011000、000000,按 3 + 6 + 6 + 6 位拼成 000011111011000000000,十六进制 1F600。这两条加起来正好说明:首字节的前导 1 越多,可用位数越多,能表示的码点范围越大。

首字节判型与字节数对照表

下表按 RFC 3629 的合法区间逐档列出。注意「合法首字节范围」这一列——它不是简单的 c0–ff,而是被规范收紧过的,这一段正是非法序列判定的全部依据。

首字节形态合法首字节总字节数码点范围有效位分配典型字符
0xxxxxxx00–7F1U+0000–U+007F7A 0F
110xxxxxC2–DF2U+0080–U+07FF5 + 6© U+00A9
1110xxxxE0–EF3U+0800–U+FFFF4 + 6 + 6中 柳
11110xxxF0–F44U+10000–U+10FFFF3 + 6 + 6 + 6😀 U+1F600
10xxxxxx——不能作首字节6(仅作续字节)单出现即 U+FFFD
1100000xC0–C1—非法(必然过长编码)—C0 80 → 两个替换字符
11111xxxF5–FF—非法(超出 U+10FFFF)—F5 80 80 80 → 四个替换字符

还有几条被规范收窄的边界,光看首字节范围看不出来,需要连着第二个字节一起判:E0 后的第一个续字节必须在 A0–BF 之间,否则就是过长编码;ED 后的第一个续字节必须在 80–9F 之间,否则会落到代理区;F0 后必须是 90–BF;F4 后必须是 80–8F。本页的状态机按这四条逐一校验,四条之外的组合一律记 U+FFFD。

输入范围、边界与精度说明

常见问题

为什么一个汉字是 3 个 8 位串,而字母只要 1 个?

因为 UTF-8 是变长编码。ASCII 范围 U+0000–U+007F 只需要 7 个有效位,塞进 1 个字节正好;常用汉字落在 U+0800–U+FFFF,需要 16 个有效位,分成 4 + 6 + 6 三段装进 3 个字节;表情这类 U+10000 以上的字符需要 21 位,分成 3 + 6 + 6 + 6 装进 4 个字节。段落数由首字节的前导 1 个数决定。示例里 柳 是 E6 9F B3 三组,O 是 4F 一组,差别就来自这里。

UTF-8 里到底有没有「代理对」?

没有。代理对是 UTF-16 的机制:U+10000 以上的字符在 UTF-16 里被拆成两个各 16 位的码元,例如 😀 是 D83D DE00。UTF-8 不这么做,它直接用 F0 9F 98 80 四个字节表示。所以 ED A0 80 这种「长得像代理」的序列不是代理对,而是违规字节——UTF-8 里 U+D800–U+DFFF 被明文禁止。

为什么非法字节会变成「�」,一个还是好几个?

� 是替换字符 U+FFFD(UTF-8 字节 EF BF BD)。数量取决于几个字节各自都无法构成合法序列:本页按「只消费 1 字节再重新同步」处理,所以 C0 80 出 2 个、ED A0 80 出 3 个、F4 90 80 80 出 4 个。这和浏览器 TextDecoder 的输出一致,可以拿它对照验证。

能用它解 GBK 编码的中文吗?

不能,而且会满屏替换字符。GBK 用 2 个字节表示一个汉字(例如「中」是 D6 D0),高位字节范围是 81–FE,在 UTF-8 看来大多是非法首字节或落不进的组合。如果手里确定是 GBK 字节流,应该先用 GBK 解码器还原成文字,再按 UTF-8 重新编码,而不是直接喂给本页。

输入里没有分隔符,一整串能不能认?

能。工具先把所有 0/1 抽出来,再按每 8 位切一刀。所以 111001101001111110110011 和带空格的写法等价。反过来,如果你的串是按「每行一个字节」粘的,换行也会被当作分隔符,结果完全一样。

为什么 C0 80 不算合法的 U+0000?

因为它是过长编码。U+0000 的正确 UTF-8 写法是单字节 00,用两个字节 C0 80 表示同一个码点是冗余的,RFC 3629 明确禁止。这条规则不是洁癖:如果允许过长编码,同一个字符会有多种字节表示,做签名校验、黑名单匹配、路径规范化时就会出现绕过漏洞,所以历史上被专门堵掉了。

表格里的「掩码取值」一列有什么用?

它是从字节到码点的中间步骤,方便你手工复核。首字节 E6 的低 4 位是 0110,两个续字节各贡献 6 位,拼起来就是码点。你可以拿这张表当教学材料:把 柳 那三行按 4 + 6 + 6 的顺序拼一遍,能得到 67F3,和码点列完全对上。填充位(首字节里那几个 x)永远是 0,不参与计算。

超过 640000 位会怎样?会帮我截断吗?

不会截断,会直接报错。截断在这里是最坏的处理方式:多字节字符被切开后,剩下的半截序列会被判成非法,于是你看到一堆 � 却不知道是自己截的。正确做法是分段处理,并且每次都让每段的有效位数保持 8 的整数倍——如果原始数据是按字符切的,字节边界天然对齐,就不会出问题。

相关工具