Проблемы с распаковкой Comp-3 в .Net. Внутри значения Comp-3 есть буквенные символы, кроме символа знака.

Я пытаюсь импортировать EDI-файл мейнфрейма обратно в SQL Server с помощью .NET, и у меня возникают проблемы с распаковкой некоторых полей comp-3.

Этот файл был от одного из наших клиентов, и у меня есть макет книги для копирования для следующих полей:

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.

Я просто сосредоточусь на этих трех полях, поскольку все остальные поля являются PIC (X) и уже являются значениями Unicode. Я загрузил все с помощью этого инструмента 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.

Тип данных Binary Coded Decimal (BCD) полностью соответствует своему названию — это значение, хранящееся в десятичной системе счисления (с основанием десять), и каждая цифра закодирована в двоичном формате. Так как цифра имеет только десять возможных значений (0-9).

Младший полубайт младшего значащего байта используется для хранения знака числа. Этот полубайт хранит только знак, а не цифру. C hex положительный, D hex отрицательный, а F hex беззнаковый.

Поскольку я знаю, что 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

Эти значения имеют другие символы помимо символа знака. У меня такое ощущение, что данные уже были преобразованы, потому что если это не так, то там не должно быть читаемых значений, если только вы не примените кодировку. Я все еще не уверен в этом и хотел бы получить любую информацию по этому поводу. Спасибо.


person Sirch Dcmp    schedule 04.01.2021    source источник
comment
Поля, которые вы пытаетесь преобразовать, выглядят поврежденными. Прошли ли они преобразование Ebcdic в Ascii ???, похоже, что могли.   -  person Bruce Martin    schedule 04.01.2021
comment
Привет Брюс, Спасибо за ответ на это. Я также предполагаю, что данные повреждены, но не был уверен, так ли это, поскольку я также получал некоторые правильные значения. Эти данные были предоставлены нашим клиентом, и экспортом этих файлов занималась сторонняя фирма. Есть ли какое-то объяснение, почему некоторые данные верны, а некоторые нет? Возможно, это проблема экспорта? Спасибо.   -  person Sirch Dcmp    schedule 04.01.2021
comment
Также я действительно не мог сразу предположить, что данные повреждены, потому что были правильные значения, и все значения имеют знак в конце. Если бы было преобразование из Ebcdic в Ascii, я бы предположил, что будут некоторые значения, которые не будут иметь символа знака в конце. Я действительно не уверен в этом, но прав ли я в этом предположении? Спасибо.   -  person Sirch Dcmp    schedule 04.01.2021
comment
Любые упакованные десятичные поля должны быть преобразованы в формат отображения, т. е. в обычный текст, ПЕРЕД любым переводом ebcdic-ascii. Это преобразование видит только биты или байты, а не текст или числа, поэтому преобразует данные соответствующим образом, независимо от того, как человек относится к данным.   -  person NicC    schedule 04.01.2021
comment
Спасибо за ответ NicC. Однако проблема заключается в том, что сторонний поставщик не будет изменять предоставленный формат.   -  person Sirch Dcmp    schedule 04.01.2021
comment
Что было бы полезно, так это сделать шестнадцатеричный дамп исходных данных в z/OS и то же самое для данных, которые вы просматриваете на платформе Windows. Вы можете подтвердить, что неявного преобразования не было. Например, если вы сделали scp, для вас будет сделано преобразование. FTPS как Binary лучше, или существующее решение может что-то делать. В z/OS вы можете просмотреть файл в ISPF, а затем ввести hex в командной строке, чтобы просмотреть шестнадцатеричные значения данных и сделать снимок экрана.   -  person Hogstrom    schedule 04.01.2021
comment
Спасибо за ответ Хогстром. На самом деле это то, что я хотел бы сделать прямо сейчас, поскольку у меня действительно НЕТ возможности проверить это, поскольку я совершенно уверен, что произошло преобразование. Я просто проверю и согласую с клиентом, если это возможно. Спасибо за вашу помощь!   -  person Sirch Dcmp    schedule 04.01.2021
comment
Еще один комментарий к методу преобразования. Есть 4 шестнадцатеричных кода для знаков. Они задокументированы здесь: ibm.com/support/knowledgecenter /SS6SG3_6.3.0/pg/ref/ x'C' — положительный, x'F' — беззнаковый, x'D' и x'B' — отрицательный. Я не думаю, что когда-либо видел x'B' в дикой природе, но ваш код должен быть обновлен, чтобы приспособиться к этому варианту использования.   -  person Hogstrom    schedule 04.01.2021
comment
Наряду с предыдущим комментарием о знаках ... это из главы z / Принципы работы - Десятичные коды: Альтернативные коды знаков также распознаются как действительные в позиции знака: 1010, 1110 и 1111 - альтернативные коды для плюса, и 1011 — это альтернативный код минуса. Альтернативные знаковые коды принимаются для любого десятичного исходного операнда, но не генерируются в завершенном результате десятично-арифметической инструкции.   -  person Hogstrom    schedule 04.01.2021


Ответы (1)


Во-первых, PIC X не является Unicode в COBOL.

Цитирую себя из здесь ...

Обычно данные мейнфрейма включают в одну запись как текстовые, так и двоичные данные, например имя, сумму в валюте и количество:

Hopper Grace ar% .

...что было бы...

x'C8969797859940404040C799818385404040404081996C004B'

... в шестнадцатеричном формате. Это кодовая страница 37, обычно называемая EBCDIC.

[...] Преобразовав кодовую страницу 1250, обычно используемую в Microsoft Windows, вы получите...

x'486F707065722020202047726163652020202020617225002E'

...где текстовые данные переводятся, но упакованные данные уничтожаются. Упакованные данные больше не имеют действительного знака в последнем полубайте (младшая половина последнего байта), сама сумма валюты была изменена, как и количество (с десятичного числа 75 на десятичное число 11 776 из-за преобразования кодовой страницы и искажения число с прямым порядком байтов как число с прямым порядком байтов).

Вероятно, ваши данные были преобразованы в кодовую страницу при передаче с мейнфрейма. Если вы знаете исходную кодовую страницу и кодовую страницу, в которую она была преобразована, вы можете расшифровать упакованные данные.

Я говорю может, потому что, если вам повезет, ваши шестнадцатеричные значения будут сопоставлены один к одному с шестнадцатеричными значениями исходной кодовой страницы. Обратите внимание, что EBCDIC x'15' и x'0D' обычно сопоставляются с ASCII x'0D'.

person cschneid    schedule 04.01.2021
comment
Спасибо за ваше понимание cschneid. Я также предполагаю, что текущие данные, которые были отправлены нам, уже преобразованы в кодовую страницу 1250, поскольку большинство полей уже доступны для чтения, за исключением упакованных полей. Обычно, если это двоичный формат EBCDIC, это будет невозможно прочитать, если не применяется кодировка. Мне может не повезти, поскольку шестнадцатеричные значения в текущем файле, похоже, не сопоставляются один к одному. Возможно, мне придется согласовать это с Клиентом и запросить необработанный двоичный файл. Большое спасибо за вашу помощь в этом! - person Sirch Dcmp; 04.01.2021