Распаковка упакованных десятичных знаков EBCDIC (COMP-3) в преобразовании ASCII

Я использую реализацию EBCDIC Джона Скита в.NET для чтения файла VSAM, загруженного в двоичном режиме по FTP из системы мэйнфреймов. Он хорошо работает для чтения / записи в этой кодировке, но не имеет ничего для чтения упакованных десятичных значений. Мой файл содержит их, и мне нужно распаковать их (очевидно, за счет увеличения байтов).

Как я могу это сделать?

Мои поля определены как PIC S9(7)V99 COMP-3.

2 ответа

Решение

Ааа, BCD. Хонк, если вы использовали его в 6502 сборке.

Конечно, лучший выбор - позволить COBOL MOVE сделать всю работу за вас! Одна из этих возможностей может помочь.

(Возможность № 1) Предполагая, что у вас есть доступ к мэйнфрейму и исходному коду, а выходной файл предназначен ТОЛЬКО для вашего использования, измените программу так, чтобы она просто ПЕРЕМЕЩАЛА значение в обычную распакованную PIC S9 (7) V99.

(Возможность №2) Если предположить, что это не так просто (например, файл вводится для других программ или не может изменить код), вы можете написать в системе другую программу на языке COBOL, которая читает этот файл и записывает другой. Вырежьте и вставьте макет записи файла с BCD в новую программу для входных и выходных файлов. Измените выходную версию, чтобы она была неупакованной. Прочитайте запись, сделайте "движение соответствующее", чтобы передать данные, и пишите до eof. Затем перенесите этот файл.

(Возможность № 3) Если вы не можете коснуться мэйнфрейма, обратите внимание на описание в статье, на которую вы ссылались в своем комментарии. BCD относительно прост. Это может быть так просто, как это (vb.net):

Private Function FromBCD(ByVal BCD As String, ByVal intsz As Integer, ByVal decsz As Integer) As Decimal
    Dim PicLen As Integer = intsz + decsz
    Dim result As Decimal = 0
    Dim val As Integer = Asc(Mid(BCD, 1, 1))
    Do While PicLen > 0
        result *= 10D
        result += val \ 16
        PicLen -= 1
        If PicLen > 0 Then
            result *= 10D
            result += val Mod 16
            PicLen -= 1
            BCD = Mid(BCD, 2)
        End If
        val = Asc(Mid(BCD, 1, 1))
    Loop
    If val Mod 16 = &HD& Then
        result = -result
    End If
    Return result / CDec(10 ^ decsz)
End Function

Я проверил это с несколькими вариантами этого вызова:

MsgBox(FromBCD("@" & Chr(13 + 16), 2, 1))

Например, -40,1. Но только несколько. Так что это все еще может быть неправильно.

Итак, если ваш comp-3 запустится, скажем, в байте 10 макета входной записи, это решит это:

dim valu as Decimal = FromBCD(Mid(InputLine,10,5), 7,2))

Принимая во внимание формулы из статьи о преобразовании данных для количества байтов для отправки и числа 9 до и после V.

Сохраните результат в десятичном формате, чтобы избежать ошибок округления. Esp, если это $$$. Float & Double вызовет у вас горе! Если вы не обрабатываете это, даже строка лучше.

конечно это может быть сложнее. Там, где я работаю, базовый блок составляет 9 бит на байт. Серьезное. Вот что делает первые 2 возможности настолько заметными. Конечно, что действительно делает их лучше, так это тот факт, что вы можете быть программистом только для ПК, и это отличный повод, чтобы заставить программиста для мэйнфреймов сделать работу за вас! Если вам так повезло, что у вас есть такая возможность...

Мир, -Ал

Я использую этот метод расширения для преобразования упакованного десятичного числа (BCD):

          /// <summary>
    /// computes the actual decimal value from an IBM "Packed Decimal" 9(x)v99 (COBOL COMP-3) format
    /// </summary>
    /// <param name="value">byte[]</param>
    /// <param name="precision">byte; decimal places, default 2</param>
    /// <returns>decimal</returns>
    public static decimal FromPackedDecimal(this byte[] value, byte precision = 2)
    {
        if (value.Length < 1)
        {
            throw new System.InvalidOperationException("Cannot unpack empty bytes.");
        }
        double power = System.Math.Pow(10, precision);
        if (power > long.MaxValue)
        {
            throw new System.InvalidOperationException(
                $"Precision too large for valid calculation: {precision}");
        }
        string hex = System.BitConverter.ToString(value).Replace("-", "");
        var bytes = Enumerable.Range(0, hex.Length)
                 .Select(x => System.Convert.ToByte($"0{hex.Substring(x, 1)}", 16))
                 .ToList();
        long place = 1;
        decimal ret = 0;
        for (int i = bytes.Count - 2; i > -1; i--)
        {
            ret += (bytes[i] * place);
            place *= 10;
        }
        ret /= (long)power;
        return (bytes.Last() & (1 << 7)) != 0 ? ret * -1 : ret;
    }
Другие вопросы по тегам