在 .Net 中解压 Comp-3 时遇到问题 Comp-3 值中除符号字符外还有字母字符

问题描述

我正在尝试使用 .NET 将大型机 EDI 文件导入回 SQL Server,但在解压某些 comp-3 字段时遇到问题。

该文件来自我们的一位客户,我有以下字段的 Copy Book 布局:

05  EH-GROSS-INVOICE-AMT            PIC S9(07)V9999  COMP-3.         
05  EH-CASH-DISCOUNT-AMT            PIC S9(07)V9999  COMP-3.         
05  EH-CASH-DISCOUNT-PCT            PIC S9(03)V9999  COMP-3.

我将只关注这 3 个字段,因为所有其他字段都是 PIC(X) 并且已经是 Unicode 值。我在 Max Vagner 创建的这个工具 Ebcdic2Ascii 的帮助下加载了所有内容。我只是对“解包”功能做了一点修改,已经修改为

private string Unpack(byte[] packedBytes,int decimalPlaces,out bool isParsedSuccessfully)
{
    isParsedSuccessfully = true;
    return BitConverter.ToString(packedBytes);
}

为了让我得到以下示例数据:

EH-GROSS-INVOICE-AMT     EH-CASH-DISCOUNT-AMT     EH-CASH-DISCOUNT-PCT
----------------------------------------------------------------------
00-1A-1A-03-26-0C        00-00-00-00-00-0C        00-00-00-0C
00-0A-1A-1A-00-0C        00-00-1A-1A-2D-0C        00-1A-00-0C
00-09-10-20-00-0C        00-00-10-1A-1A-0C        00-1A-00-0C

这是我根据我对 Comp-3 值的理解为解包这些值而创建的示例代码:

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var result1 = UnpackMod("00-1A-1A-03-26-0C",4);
            var result2 = UnpackMod("00-00-00-00-00-0C",4);
            var result3 = UnpackMod("00-00-00-0C",4);

            Console.WriteLine($"{result1}\n{result2}\n{result3}\n");

            var result4 = UnpackMod("00-0A-1A-1A-00-0C",4);
            var result5 = UnpackMod("00-00-1A-1A-2D-0C",4);
            var result6 = UnpackMod("00-1A-00-0C",4);

            Console.WriteLine($"{result4}\n{result5}\n{result6}\n");

            var result7 = UnpackMod("00-09-10-20-00-0C",4);
            var result8 = UnpackMod("00-00-10-1A-1A-0C",4);
            var result9 = UnpackMod("00-1A-00-0C",4);

            Console.WriteLine($"{result7}\n{result8}\n{result9}");

            Console.ReadLine();
        }

        /// <summary>
        /// Method for unpacking Comp-3 fields.
        /// </summary>
        /// <param name="hexString"></param>
        /// <param name="decimalPlaces"></param>
        /// <returns>Returns numeric string if parse was successful; else Return input hex string</returns>
        private static string UnpackMod(string inputString,int decimalPlaces)
        {
            var outputString = inputString;

            // Remove "-".
            outputString = outputString.Replace("-","");

            // Check last character for sign.
            string lastChar = outputString.Substring(outputString.Length - 1,1);
            bool isNegative = (lastChar == "D" || lastChar == "B");

            // Remove sign character.
            if (lastChar == "C" || lastChar == "A" || lastChar == "E" || lastChar == "F" || lastChar == "D" || lastChar == "B")
            {
                outputString = outputString.Substring(0,outputString.Length - 1);
            }

            // Place decimal point.
            outputString = outputString.Insert(outputString.Length - decimalPlaces,".");

            // Check if parsed value is numeric. This will also eliminate all leading 0.
            var isParsedSuccessfully = decimal.TryParse(outputString,out decimal decimalValue);

            // If isParsedSuccessfully is true then return numeric string else return inputString..
            string result = "NULL";
            if (isParsedSuccessfully)
            {
                // Convert value to negative.
                if (isNegative)
                {
                    decimalValue = decimalValue * -1;
                }

                result = decimalValue.ToString();
            }

            return result;
        }
    }
}

运行示例代码后,我能够得到以下结果:

EH-GROSS-INVOICE-AMT     EH-CASH-DISCOUNT-AMT     EH-CASH-DISCOUNT-PCT
----------------------------------------------------------------------
NULL                     0.0000                   0.0000
NULL                     NULL                     NULL
9102.0000                NULL                     NULL        

如您所见,我只能正确获得以下 3 个值:

00-09-10-20-00-0C -> 9102.0000
00-00-00-00-00-0C -> 0.0000
00-00-00-0C       -> 0.0000

从这个来源引用:http://www.3480-3590-data-conversion.com/article-packed-fields.html。我对 Comp-3 的理解如下:

COBOL Comp-3 是一种二进制字段类型,它使用称为二进制编码十进制或 BCD 的表示法将两个数字放入(“打包”)每个字节中。

二进制编码十进制 (BCD) 数据类型正如其名称所暗示的那样——它是以十进制(基数为 10)表示法存储的值,并且每个数字都是二进制编码的。因为一个数字只有十个可能的值(0-9)。

最低有效字节的低半字节用于存储数字的符号。这个半字节只存储符号,而不是数字。 “C”十六进制为正,“D”十六进制为负,“F”十六进制为无符号。

因为我知道 BCD 应该只有 0-9 的值,并且最后应该只有一个字符,可以是“C”、“D”或“F”。我不知道如何解压以下值:

00-1A-1A-03-26-0C
00-0A-1A-1A-00-0C        
00-00-1A-1A-2D-0C
00-1A-00-0C
00-00-10-1A-1A-0C
00-1A-00-0C

这些值除了符号字符之外还有其他字符。我有一种感觉,数据已经被转换,因为如果不是,那么除非您应用编码,否则那里应该没有可读值。我仍然不确定这一点,并希望对此有任何见解。谢谢。

解决方法

首先,PIC X 在 COBOL 中不是 Unicode。

引用here...

大型机数据通常包括文本和二进制数据 在单个记录中,例如姓名、货币金额和 数量:

Hopper Grace ar% .

...这将是...

x'C8969797859940404040C799818385404040404081996C004B'

...十六进制。这是代码页 37,通常称为 EBCDIC。

[...]转换为代码页 1250,通常在 Microsoft 上使用 Windows,你最终会...

x'486F707065722020202047726163652020202020617225002E'

...文本数据被翻译但打包数据被破坏。 打包的数据在最后一个半字节( 最后一个字节的下半部分),货币金额本身已经 更改了数量(从十进制 75 到十进制 11,776,由于 代码页转换和大端数字的重整作为 小端数)。

很可能您的数据是在从大型机传输时转换的代码页。如果您知道原始代码页和它被转换成的代码页,那么您可能就能够解读打包的数据。

我说可能是因为,如果幸运的话,您拥有的十六进制值将与原始代码页中的十六进制值一对一映射。请注意,EBCDIC x'15' 和 x'0D' 都映射到 ASCII x'0D' 是很常见的。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...