问题出在这个表达式中:
new String(letters.getBytes('IBM500'))
letters.getBytes 创建一个字节数组,其中包含(十六进制):
81 82 83 84 C1 C2 C3 C4
但随后您会立即使用您的平台默认编码将其转换回 Unicode 字符串:
new String( <byte-array> );
如果您希望字符串中字符的序数值等于字节值,则必须指定执行此操作的编码,例如 ISO-8859-1:
new String(letters.getBytes('IBM500'), "ISO-8859-1")
您使用的编码没有为字节定义字符编码,81
因此它用 ?
( 3f
) 替换它。您很可能使用的是Windows-1252。
字符串包含字符,而不是字节。当从一个到另一个时,Java 将始终应用编码转换。
编辑:回复@mister270 的评论:
这是一个Java程序来演示:
public class Ebcdic
{
public static void main(String[] args) throws Exception
{
String letters = "abcdABCD";
byte[] ebcdic = letters.getBytes("IBM500");
System.out.print("Ebcdic bytes:");
for (byte b: ebcdic)
{
System.out.format(" %02X", b & 0xFF);
}
System.out.println();
String lettersEbcdic = new String(ebcdic, "ISO-8859-1");
System.out.print("Ebcdic bytes stored in chars:");
for (char c: lettersEbcdic.toCharArray())
{
System.out.format(" %04X", (int) c);
}
System.out.println();
System.out.println("Ebcdic bytes in chars printed in using my default platform encoding: " + lettersEbcdic);
}
}
输出是:
Ebcdic bytes: 81 82 83 84 C1 C2 C3 C4
Ebcdic bytes stored in chars: 0081 0082 0083 0084 00C1 00C2 00C3 00C4
Ebcdic bytes in chars printed in using my default platform encoding: ????��ǎ
这表明
- 使用“IBM500”正确地将 Ebcdic 转换为字节数组
- 使用“ISO-8859-1”将字节“身份”转换为字符的过程正确发生
- 我的系统没有将 Unicode 字符 U+0081 等转换为我的默认平台字符编码的映射,因此它显示为
?
Java(Groovy 也是如此)在内部将字符存储为 Unicode。UTF16,准确地说。如果您想将它们编码为 Ebcdic,那么它们将不再是字符并且不应再保存在字符串中。Ebcdic 是一种 8 位编码,因此每个字符都可以存储在一个字节中。如果您需要与需要特定编码的系统(在您的情况下为 Ebcdic)进行交互,那么该系统确实应该接受字节,而不是字符串,否则您最终只会遇到这些混乱。
如果您必须使用字符串来保存 Ebcdic 字节,那么无论何时使用 InputStream 或 OutputStream(包括 System.out)都必须使用 ISO-8859-1 编码,以确保您的 ebcdic 代码不会从字节“转换”为字符