EBCDIC编码页无法转换小写字母'a'。

3
我试图使用IBM代码页将一串ASCII字符转换为EBCDIC。 转换是正确的,除了小写字母'a'被转换为无法打印的字符。
以下是在Windows 7上运行的groovy脚本的一部分,说明了这个问题。
groovy:000> letters='abcdABCD'
===> abcdABCD
groovy:000> String.format("%04x", new BigInteger(1, letters.getBytes())
===> 6162636441424344
groovy:000> lettersx=new String(letters.getBytes('IBM500'))
===> ?éâä┴┬├─
groovy:000> String.format("%04x", new BigInteger(1, lettersx.getBytes()))
===> 3f828384c1c2c3c4

将字符串转换为EBCDIC后,除第一个字符外,所有字符均有效,第一个字符是小写字母'a'。无论如何我都找不到有关此问题的信息。我尝试了许多IBM代码页,但结果相同(IBM01140、IBM1047等)。

1个回答

3
问题出在这个表达式中:
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 代码未从字节转换为字符。

Adrian,我理解你的意思,但使用ISO-8859-1会导致所有小写字母都返回为x'3f'。 - mister270
如果这个回答解决了你的问题,你可以点击答案顶部旁边的 (参见FAQ)。 - Adrian Pronk
完成@adrianpronk,但我也找到了这个答案,它更详细地解释了整个问题。如果我先看到这个答案,可能就不会发帖了。 - mister270

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接