Java UTF到EBCDIC的转换是无损的吗?

Java UTF到EBCDIC的转换是无损的吗?,java,ibm-mq,ebcdic,Java,Ibm Mq,Ebcdic,我们有一个通过MQ与外部服务器通信的进程。外部系统在大型机(IBMz/OS)上运行,而我们在CentOS Linux平台上运行我们的进程。到目前为止,我们从未遇到过任何问题 最近,我们开始从他们那里接收消息,消息中嵌入了不可打印的EBCDIC字符。他们使用字符作为压缩ID,长度为8字节。当我们接收到它时,它到达我们的UTF编码队列(CCSID 1208) 为了识别我们的响应消息,他们需要将原始的8个字节返回。我试图在Java中找到一个解决方案,在发送响应之前将ID从UTF转换回EBCDIC 我一

我们有一个通过MQ与外部服务器通信的进程。外部系统在大型机(IBMz/OS)上运行,而我们在CentOS Linux平台上运行我们的进程。到目前为止,我们从未遇到过任何问题

最近,我们开始从他们那里接收消息,消息中嵌入了不可打印的EBCDIC字符。他们使用字符作为压缩ID,长度为8字节。当我们接收到它时,它到达我们的UTF编码队列(CCSID 1208)

为了识别我们的响应消息,他们需要将原始的8个字节返回。我试图在Java中找到一个解决方案,在发送响应之前将ID从UTF转换回EBCDIC

我一直在使用JTOpen库,使用AS400Text类进行转换。此外,交易对手已向我们发送了以字节为单位的ID快照。但是,当我比较转换后的字节时,它们与原始消息不同

有没有人遇到过这个问题?也许我用错代码页了

感谢您的任何意见

来自交易对手的字节(位置[5,14]):

程序输出:

UTF String: [R030ôîÕ؜IDMDHP1027W 0510]
EBCDIC String: [R030ôîÃÃÂIDMDHP1027W 0510]
NATIVE CHARSET - HEX:     [52303330C3B4C3AEC395C398C29C491006444D44485031303237572030353130] 
CP500 CHARSET  - HEX:     [D9F0F3F066BE66AF663F663F623FC9102EC4D4C4C8D7F1F0F2F7E640F0F5F1F0] 
以下是一些示例代码:

private void readAndPrint(MQMessage mqMessage) throws IOException {
    mqMessage.seek(150);
    byte[] subStringBytes = new byte[32];
    mqMessage.readFully(subStringBytes);

    String msgId = toHexString(mqMessage.messageId).toUpperCase();

    System.out.println("----------------------------------------------------------------");
    System.out.println("MESSAGE_ID: " + msgId);

    String hexString = toHexString(subStringBytes).toUpperCase();
    String subStr = new String(subStringBytes);
    System.out.println("NATIVE CHARSET - HEX:     [" + hexString + "] [" + subStr + "]");

    // Transform to EBCDIC
    int codePageNumber = 37;
    String codePage = "CP037";

    AS400Text converter = new AS400Text(subStr.length(), codePageNumber);
    byte[] bytesData = converter.toBytes(subStr);
    String resultedEbcdicText = new String(bytesData, codePage);

    String hexStringEbcdic = toHexString(bytesData).toUpperCase();
    System.out.println("CP500 CHARSET  - HEX:     [" + hexStringEbcdic + "] [" + resultedEbcdicText + "]");

    System.out.println("----------------------------------------------------------------");
}

如果MQ消息具有不同的子消息字段,需要不同的编码,那么这就是您应该如何处理这些消息,即作为单独的消息片段

但正如您所描述的,整个消息需要在不进行转换的情况下接收。前八个字节需要分别提取和保存。然后可以转换消息的其余部分的编码(除非其他子字段也需要提取为二进制的未转换字节)

对于任何返回消息,必须进行相反的转换。可以转换消息的文本部分,然后子字符串可以在其前面加上原始的八个字节。然后,新重构的消息可以通过队列发回,而无需自动转换


另一端的合作伙伴未正确使用消息传递产品。(当然,你可能不应该大声说出来。)这样的信息中不应该有任何部分不能在两个方向上自动完整地保存下来。对于一个示例方法,它应该表示为更像8字节值的16字节十六进制表示,而不是8字节二进制字段。在十六进制中,通过路由的任何一种方式都不会有转换问题。

在我看来,特殊的8字节实际上不是EBCDIC字符,而只是8字节的数据。如果是这种情况,那么我相信,正如另一个答案所提到的,您应该单独处理这8个字节,而不允许它转换为UTF8,然后返回EBCDIC进行进一步处理

根据您使用的EBCDIC变体,EBCDIC中的某个字节很可能没有转换为有意义的UTF-8字符,因此,您将无法通过将UTF8字符转换为您收到的EBCDIC来获取原始字节


在Google上搜索一下,我会找到几个EBCDIC表(例如)。您可以看到EBCDIC中有许多值没有指定字符。因此,当它们转换为UTF8时,您可能不会假设它们中的每一个都将转换为Unicode中的不同字符。因此,您建议的处理方式将非常危险且容易出错。

新字符串(subStringBytes)-这是使用默认编码。你知道它是什么吗?你知道它支持所有可能的字节组合吗?你知道它是否是可逆的吗?而且,如果没有后缀,“UTF”是没有意义的。你说的是UTF-8吗?如果是这种情况,那么答案显然是否定的,因为并非所有字节序列在中都是合法的——包括消息的前三个字节。MQ中的CCSID 1208对应于UTF-8()。当你说并非所有字节序列都是合法的,你的意思是因为UTF-8是可变宽度的吗?不仅仅是因为它是可变宽度的,而且高阶位有意义(因此我喜欢维基百科页面)。您显示的“消息ID”以
C3E2
开头,这是一个无效的UTF-8序列:
C3
是一个两字节序列的开头,但
E2
不是一个有效的第二字节;它仅作为3字节序列的第一个字节有效。我想再次指出,
新字符串(subStringBytes)
使用您的平台默认编码。也许这是你的UTF-8,也许不是。更糟糕的是,对于您来说,它可能是UTF-8,而不是在您用于部署的任何平台上的UTF-8。消息已转换为UTF-8,但随后EBCDIC转换丢失。我们最终使用了从UTF-8到ASCII变量的转换,然后返回到EBCDIC。
private void readAndPrint(MQMessage mqMessage) throws IOException {
    mqMessage.seek(150);
    byte[] subStringBytes = new byte[32];
    mqMessage.readFully(subStringBytes);

    String msgId = toHexString(mqMessage.messageId).toUpperCase();

    System.out.println("----------------------------------------------------------------");
    System.out.println("MESSAGE_ID: " + msgId);

    String hexString = toHexString(subStringBytes).toUpperCase();
    String subStr = new String(subStringBytes);
    System.out.println("NATIVE CHARSET - HEX:     [" + hexString + "] [" + subStr + "]");

    // Transform to EBCDIC
    int codePageNumber = 37;
    String codePage = "CP037";

    AS400Text converter = new AS400Text(subStr.length(), codePageNumber);
    byte[] bytesData = converter.toBytes(subStr);
    String resultedEbcdicText = new String(bytesData, codePage);

    String hexStringEbcdic = toHexString(bytesData).toUpperCase();
    System.out.println("CP500 CHARSET  - HEX:     [" + hexStringEbcdic + "] [" + resultedEbcdicText + "]");

    System.out.println("----------------------------------------------------------------");
}