Преобразование EBCDIC в ASCII, обработка числовых значений

Я пытаюсь преобразовать файлы из формата ECDIC в формат ASCII и столкнулся с интересной проблемой. Файлы содержат записи фиксированной длины, некоторые поля которых представляют собой целые двоичные числа со знаком (обозначаются как B4 в макете записи) и числовые значения высокой точности (обозначаются как L8 в записи). макет). Мне удалось преобразовать символьные данные без проблем, но я не знаю, как преобразовать эти числовые значения. Из справочного руководства для исходной системы (IBM 5110), поля описаны ниже.

B указывает длину (2, 4 или 8 байтов) числовых элементов данных в двоичном целочисленном формате с фиксированной точкой со знаком, которые должны быть преобразованы во внутренний формат данных BASIC. Для записи файла ввода-вывода следующие 2, 4 или 8 байтов в записи содержат двоичное значение со знаком, которое система преобразует во внутренний формат данных и присваивает переменной (переменным), указанным в READ FILE или REREAD. Оператор FILE с использованием оператора FORM.

и

L указывает длинную точность (8 символов) для числовых значений. Для ввода эта запись указывает, что восьмипозиционное значение длинной точности в записи должно быть присвоено без преобразования соответствующей числовой переменной, указанной в операторе READ FILE или REREAD FILE.

РЕДАКТИРОВАТЬ: вот код, который я использую для преобразования

private void ConvertFile(EbcdicFile file)
{
    if (file == null) return;

    var filePath = Path.Combine(file.Path, file.FileName);
    if (!File.Exists(filePath))
    {
        this.Logger.Info(string.Format("Cannot convert file {0}. It does not exist.", filePath));
        return;
    }

    var ebcdic = Encoding.GetEncoding(37);
    string convertedFilepath = Path.Combine(file.Path, file.ConvertedFileName);
    byte[] fileData = File.ReadAllBytes(filePath);

    if (!file.HasNumericFields)
        File.WriteAllBytes(convertedFilepath, Encoding.Convert(ebcdic, Encoding.ASCII, fileData));
    else
    {
        var convertedFileData = new List<byte>();
        for (int position = 0; position < fileData.Length; position += file.RecordLength)
        {
            var segment = new ArraySegment<byte>(fileData, position, file.RecordLength);
            file.Fields.ForEach(field =>
                {
                    var fieldSegment = segment.Array.Skip(segment.Offset + field.Start - 1).Take(field.Length);
                    if (field.Type.Equals("string", StringComparison.OrdinalIgnoreCase))
                    {
                        convertedFileData.AddRange(
                            Encoding.Convert(ebcdic, Encoding.ASCII, fieldSegment.ToArray())
                            );
                    }
                    else if (field.Type.Equals("B4", StringComparison.OrdinalIgnoreCase))
                    {
                        // Not sure how to convert this field
                    }
                    else if (field.Type.Equals("L8", StringComparison.OrdinalIgnoreCase))
                    {
                        // Not sure how to convert this field
                    }
                });
        }

        File.WriteAllBytes(convertedFilepath, convertedFileData.ToArray());
    }
}

person Garett    schedule 09.01.2013    source источник
comment
Вы описали свою текущую ситуацию, но не задали вопрос :-). Я думаю, вы хотите сказать, как мне преобразовать типы полей B4 и L8 из файлов данных, написанных на IBM 5110 BASIC.... Вам нужно показать нам некоторые примеры данных (шестнадцатеричный дамп) и, желательно, также правильную интерпретацию.   -  person Ben    schedule 09.01.2013


Ответы (2)


Сначала вы должны знать фиксированный размер записи. Используйте FileStream.Read() для чтения одной записи в байтах. Затем Encoding.GetString(), чтобы преобразовать его в строку.

Затем извлеките поля из записи, используя String.SubString(). B4 — это просто вызов SubString длиной 4, L8 длиной 8. Далее преобразуйте такое поле в число с помощью Decimal.Parse(). Возможно, вам придется разделить результат, было неясно, какой множитель с фиксированной точкой используется. Хорошие шансы на 100.

person Hans Passant    schedule 09.01.2013
comment
Спасибо за ответ. Я считаю, что то, что вы описали, - это то, как я это реализовал. Хотя я обрабатываю каждую запись как массив байтов. Я рассмотрю использование Decimal.Parse с множителем с фиксированной точкой. Это хорошее предложение. К сожалению, у меня есть документация только для макетов записей и вывода, так что это метод проб и ошибок. - person Garett; 09.01.2013

Итак, я понял, как преобразовать оба поля. Поля B4 очень просты. По сути, это 4-байтовый массив, который можно преобразовать в целое число.

//The IBM 5110 were big endian machines, so reverse the array 
if (BitConverter.IsLittleEndian)
    Array.Reverse(by);

int value = BitConverter.ToInt32(by, 0);

Поля L8 представляют собой 8-байтовые массивы, представляющие IBM Double Precision Float. . Существует много способов преобразовать его в число с плавающей запятой IEEE 754. Несколько примеров можно найти по адресу:

Вот версия, которую я использовал на основе рекомендаций из статей.

private double IbmFloatToDouble(byte[] value)
{
    if (ReferenceEquals(null, value))
        throw new ArgumentNullException("value");

    if (BitConverter.ToInt64(value, 0) == 0)
        return 0;

    int exponentBias = 64;
    int ibmBase = 16;
    double sign = 0.0D;

    int signValue = (value[0] & 0x80) >> 7;
    int exponentValue = (value[0] & 0x7f);
    double fraction1 = (value[1] << 16) + (value[2] << 8) + value[3];
    double fraction2 = (value[4] << 24) + (value[5] << 16) + (value[6] << 8) + value[7];
    double exponent24 = 16777216.0;             // 2^24
    double exponent56 = 72057594037927936.0;    // 2^56

    double mantissa1 = fraction1 / exponent24;
    double mantissa2 = fraction2 / exponent56;
    double mantissa = mantissa1 + mantissa2;
    double exponent = Math.Pow(ibmBase, exponentValue - exponentBias);

    if (signValue == 0) 
        sign = 1.0;
    else 
        sign = -1.0;

    return (sign * mantissa * exponent);
}
person Garett    schedule 02.03.2013