这是因为 ISO-8859-1 的编码特性与 UTF-8 的字节结构之间存在一种“可逆映射”关系,使得错误解码后的字符串在特定操作下能还原原始字节。以下是详细解释:
1. 核心原理:ISO-8859-1 的“字节透明性”
ISO-8859-1(Latin-1)是一种单字节编码,其编码规则为:
字符的 Unicode 码点值 = 对应的字节值(0x00~0xFF)。
例如:
- 字符
A
的 Unicode 是U+0041
→ 编码为字节0x41
。 - 字符
ä
的 Unicode 是U+00E4
→ 编码为字节0xE4
。
这意味着:任何字节(0x00~0xFF)在 ISO-8859-1 中都有唯一对应的字符,且该字符的 ISO-8859-1 编码值与原始字节值完全相同。
2. 错误解码与恢复的完整过程
假设原始 UTF-8 字节序列为 b
,但被错误地用 ISO-8859-1 解码为字符串 s
,再通过以下操作恢复:
步骤 1:UTF-8 字节 → 错误解码为 ISO-8859-1 字符串
byte[] utf8Bytes = ...; // 原始 UTF-8 字节(如 "中文" 的 UTF-8 编码)
String wrongStr = new String(utf8Bytes, "ISO-8859-1"); // 错误解码
- 每个 UTF-8 字节会被 ISO-8859-1 视为一个独立字符。
- 例如,UTF-8 的
0xE4
(对应汉字“中”的一部分)会被解码为 ISO-8859-1 字符ä
(UnicodeU+00E4
)。
步骤 2:将错误字符串重新编码为字节
byte[] recoveredBytes = wrongStr.getBytes("ISO-8859-1");
- 由于 ISO-8859-1 的“字节透明性”,每个字符的 Unicode 码点值会被直接转换为原始字节。
- 例如,字符
ä
(U+00E4
)会被编码为0xE4
,与原始 UTF-8 字节一致。
步骤 3:用 UTF-8 重新解码字节
String restoredStr = new String(recoveredBytes, "UTF-8");
- 此时
recoveredBytes
是原始 UTF-8 字节,因此能正确还原为原始字符串。
3. 为什么能恢复?关键条件
-
条件 1:原始字节必须是有效的 ISO-8859-1 字符(即字节值在
0x00~0xFF
之间)。
UTF-8 的每个字节天然满足这一条件(UTF-8 使用 1~4 字节,每字节范围0x00~0xFF
)。 -
条件 2:恢复过程中必须使用相同的 ISO-8859-1 编码重新编码字符串。
如果使用其他编码(如 GBK),字节值会改变,导致恢复失败。
4. 示例演示
假设原始 UTF-8 字符串为 "A"
(ASCII 字符):
- UTF-8 编码:
0x41
。 - 错误解码为 ISO-8859-1:字符
A
(UnicodeU+0041
)。 - 重新编码为 ISO-8859-1 字节:
0x41
。 - UTF-8 解码:恢复为
"A"
。
再比如汉字 "中"
(UTF-8 编码为 0xE4 0xB8 0xAD
):
- 错误解码为 ISO-8859-1:三个字符
ä
,¸
,
。 - 重新编码为 ISO-8859-1 字节:
0xE4 0xB8 0xAD
。 - UTF-8 解码:恢复为
"中"
。
5. 限制与风险
- 不可恢复的极端情况:如果原始字节超出 ISO-8859-1 范围(如 无效的UTF-8 字节序列)。
总结
通过 new String(wrongStr.getBytes("ISO-8859-1"), "UTF-8")
恢复的核心在于:
ISO-8859-1 的编码规则使得错误解码后的字符串的字节表示与原始 UTF-8 字节完全一致,从而允许通过重新编码和正确解码还原原始内容。这一方法本质上是利用了 ISO-8859-1 的“字节透明性”特性。