JAVA 到 EBCDIC 的转换没有发生

Posted

技术标签:

【中文标题】JAVA 到 EBCDIC 的转换没有发生【英文标题】:JAVA to EBCIDIC Conversion is not happening 【发布时间】:2020-08-23 07:01:58 【问题描述】:

我正在尝试从一个应用程序获取数据并在处理数据后将其发送到另一个(大型机)。 假设我正在获取“这是来自另一个应用程序”的数据,处理与“此数据已处理”相同的处理状态。最终消息应结合使用编码(Cp1047)到大型机应用程序读取相同

0024这是来自另一个应用程序001A此数据已处理

0024 的十进制值为 36(消息长度 + 4 是十六进制值长度) 001A 的十进制值为 26(处理后的消息长度 + 4) 我的应用程序在 Java8 上运行并使用 websphere MQ。我需要将数据发送到从 Mainframe MQ 接收数据的应用程序。 WebSphere MQ 中的远程队列将消息放入大型机 MQ 的本地队列。我的代码如下,使用 Cp1047 转换数据和编码,

String incomingData = "This is from another application";
String processingData = "This data is processed" 
public String outGoingData(String incomingData, String processingData)   
  StringBuilder strBuilder = new StringBuilder();
  return stringbuilder.append(new String(convertToEbcidie(incomingData, "Cp1047")))
  .append(incomingData)
  .append(new String(convertToEbcidie(processingData, "Cp1047")))
  .append(processing data).toString(); //playing this string to queue

private byte[] convertToEbcidic(String s) 
  String hexStr = StringUtils.leftPad(s.length+4, 8, "0");
  byte[] byteAry = new byte[hexStr.length()/2];
  for (int i = 0; i < hexStr.length(); i+=2) 
    byteAry[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                   + Character.digit(s.charAt(i+1), 16)); 
    return byteAry;

接收方应用程序(大型机)决定哪个是原始消息和基于处理状态的 4 个字符的十六进制值。 他们能够阅读大部分信息,但不是全部。例如,长度为 805 的十六进制值为 325,但在大型机 mq 条目中为 315。由于长度不匹配,它们无法处理。

另一个示例数据:- OO25这是来自来源001A已成功处理的原始数据

0025 是 org msg 长度 (33) + 4 的十六进制值,001A 是处理后的 msg 长度 (22) + 4 的十六进制值。这里 4 是十六进制值的长度。我是否缺少任何转换为​​ ebcidic 的逻辑?

【问题讨论】:

这能回答你的问题吗? ASCII to EBCDIC conversion in Java 这个问题的格式看起来比第一个问题好得多,但是您不应该打开一个新的重复问题,因为您的旧问题已关闭,您应该编辑已关闭的问题并添加这些详细信息。 您的代码页翻译正在将长度字段转换为文本的一部分,因此 0x03 映射到 EBCDIC 中的相同值,但 0x25(换行)映射到 EBCDIC 中的 0x15(新行) . 有什么办法可以防止0x25转换成0x15。随机六进制值会发生这种情况,并且由于此大型机应用程序无法读取更多消息。我尝试了许多编码选项,例如“Cp1047”,但没有取得多大成功。 在将传入消息转换为 EBCDIC 时可以跳过长度字段吗? 【参考方案1】:

您发布的代码有很多错误,我不知道从哪里开始。

首先,*** 规则/策略是您应该将您的工作代码从编辑器或 IDE 复制并粘贴到 ***。显然,您没有这样做,而只是在 *** 编辑窗口中创建了新代码——这是错误的!!!

return stringbuilder.append(new String(convertToEbcidie(incomingData, "Cp1047")))
  .append(incomingData)
  .append(new String(convertToEbcidie(processingData, "Cp1047")))
  .append(processing data).toString();

(1) convertToEbcidic 方法采用 ONE 参数,而不是 2。您的括号错误。

(2) 方法名是convertToEbcidic而不是convertToEbcidie(最后一个字母是'c'而不是'e')

private byte[] convertToEbcidic(String s) 
  String hexStr = StringUtils.leftPad(s.length+4, 8, "0");
  byte[] byteAry = new byte[hexStr.length()/2];
  for (int i = 0; i < hexStr.length(); i+=2) 
    byteAry[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                   + Character.digit(s.charAt(i+1), 16)); 
    return byteAry;

(3) 我不知道您要做什么,但显然它没有做您认为的任何事情。您是否使用调试器单步执行代码?

(3A) s 变量的长度是一个方法而不是一个字段。它应该是“s.length()”而不是“s.length”。

(3B) StringUtils.leftPad 方法的第一个参数必须是 String 而不是 int。

(3C) hexStr 将为“00000036”(32 + 4)。 hexStr 的长度为 8。

(3D) byteAry 的大小为 4!!!您如何将 36 个字符放入 4 个字节中?即 4 + 26 个字符“这是来自另一个应用程序”。

(3E) 你的循环在做什么??它运行了 4 次,我完全不知道你在想什么。

好的。现在解决您的问题。

ANOTHER SAMPLE DATA :- OO25THIS IS ORIGINAL DATA FROM SOURCE001APROCESSED SUCCESSFULLY

好的。因此,基于该示例,在我看来布局如下:

“字符数据1”长度的十六进制字符串表示字符数据1“字符数据2”长度的十六进制字符串表示字符数据2

由于整个消息负载将是字符串,因此将字符串作为消息放在本地代码页(ASCII)中要好得多,将 MQMD 格式标记为字符串并让 MQ 进行转换。转换将在大型机应用程序发出“MQGET with Convert”调用时完成。

这是解决您问题的正确代码:

String incomingData = "This is from another application";
String processingData = "This data is processed";
StringBuilder sb = new StringBuilder();
MQQueueManager qMgr = null;
MQQueue outQ = null;

String inHexLen = Integer.toHexString(incomingData.length()+4).toUpperCase();
inHexLen = StringUtils.leftPad(inHexLen, 4, '0');
sb.append(inHexLen);
sb.append(incomingData);

String outHexLen = Integer.toHexString(processingData.length()+4).toUpperCase();
outHexLen = StringUtils.leftPad(outHexLen, 4, '0');
sb.append(outHexLen);
sb.append(processingData);

System.out.println("sb="+sb.toString());

try

   qMgr = new MQQueueManager("MQA1");

   outQ = qMgr.accessQueue("TEST.Q1",
                           CMQC.MQOO_OUTPUT + CMQC.MQOO_FAIL_IF_QUIESCING);

   MQMessage sendmsg = new MQMessage();
   sendmsg.format = CMQC.MQFMT_STRING;
   sendmsg.writeString(sb.toString());
   outQ.put(sendmsg, new MQPutMessageOptions());

catch (MQException e)

   e.printStackTrace();

catch (IOException e)

   e.printStackTrace();

finally

   try
   
      if (outQ != null)
         outQ.close();
   
   catch (MQException e)
   
      e.printStackTrace();
   
   try
   
      if (qMgr != null)
         qMgr.disconnect();
   
   catch (MQException e)
   
      e.printStackTrace();
   

【讨论】:

【参考方案2】:

您似乎尝试在消息中混合二进制数据和文本数据,而您希望这样做的方式如下:

您获取二进制字段并在客户端执行 EBCDIC->client-codepage 转换 然后将其传递给 MQ,然后由 MQ 执行客户端代码页->EBCDIC 转换 你希望你能回到你开始的地方

这可以工作,但在大多数情况下它不会,因为它需要:

您的字符转换和 MQ 的字符转换使用相同的转换表 这些转换表是双射的,即它们允许无损往返转换

特别是最后一点对于不可打印(甚至非映射)的字节值通常是不正确的。

因此,要么将数据作为不转换的二进制文件传输,要么作为带转换的文本传输,但任何将两者混合的尝试都注定会失败。

【讨论】:

由于我的数据包含单个消息的两个部分(原始消息和消息状态),那么大型机如何区分原始消息和长度不固定的消息状态。【参考方案3】:

在您发送到 zOS 的数据中,有一些 ASCII 字符和一些包含长度的字节

如果您将完整的字符串 - 组合文本字符通过代码转换,那么这就是完整字符串中的每个字节都会发生的情况 - 无论代码转换会尝试将每个字节从它的 ASCII 值转换为等效“EBCDIC”值。

因此在转换结束时,文本字符是正常的,因为它们是正常的可打印字符,但是包含长度的字节将被转换为一些 EBCDIC 字符并且不再可用于进行长度计算。

由于您在 zOS 上的应用似乎正在从 MQ 获取消息,因此您需要确保从 zOS 上的 MQ 获取消息,以便 MQ 不会为您进行任何转换。

与其在一次操作中将整个消息转换为 EBCDIC,您需要解析消息。

将消息的前两个字节转换为具有第一个文本长度的变量(无需转换),谷歌如何编写 Java 代码将其转换为 java int,然后使用该值获取那些 N MQ 字符串中的字节,然后在这些字节上转换为 EBCDIC,

然后从第一个字符串结束后的 MQ 消息中获取两个字节,并重复上述过程。

【讨论】:

@IBMerPassingBy - 我正在尝试将消息播放到队列中,但我无法控制作为大型机 merva 的目标队列管理器来修改任何更改。 我的回复没有提到更改目标队列。 大概你是在 zOS 端写代码吧?如果您然后重新阅读我上面的建议 - 因为您在 zOS 端的代码需要以上述方式工作 - 在 ZOS 端的代码中 - 不要将 MQGMO_CONVERT 设置为 MQGMO.Options 之一,以便代码在 zOS 端获取消息而不经过 ASCII 到 EBCDIC 的转换

以上是关于JAVA 到 EBCDIC 的转换没有发生的主要内容,如果未能解决你的问题,请参考以下文章

通过 USS 中的 xlc fgets() 控制从 ASCII 到 EBCDIC 的自动转换

我们有许多 EBCDIC 格式的大型机文件,Python 中有没有办法将大型机文件解析或转换为 csv 文件或文本文件?

在将 EBCDIC 转换为十六进制时需要帮助

EBCDIC 代码页不转换小写“a”

EBCDIC 到 ASCII 无法正常工作

将 VB EBCDIC 文件转换为 ASCII,其中字帖记录以 01 分隔