Как распаковать цифры COMP-3 с помощью Java?

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

упакованная цифра: ?

Я прочитал учебники по распаковке цифр и нашел следующее правило для подсчета количества байтов, необходимых для распаковки цифр:

total_number_of_bytes = (no. of digits + 1) / 2

Я написал следующий код для распаковки цифр:

public String unpackData(String packedData, int decimalPointLocation) {
        String unpackedData = "";
        char[] characters = packedData.toCharArray();
        final int impliedPositive = 15;
        final int positiveNumber = 12;
        final int negativeNumber = 13;
        for (int currentCharIndex = 0; currentCharIndex < characters.length; currentCharIndex++) {
            byte[] unpackedDigits = unpackByte((byte) characters[currentCharIndex]);
            if(currentCharIndex == (characters.length - 1)) {
                if(unpackedDigits[1] == impliedPositive || unpackedDigits[1] == positiveNumber) {
                    unpackedData += String.valueOf(unpackedDigits[0]);
                } else if(unpackedDigits[1] == negativeNumber) {
                    unpackedData = "-" + unpackedData;
                }
            } else {
                unpackedData += String.valueOf(unpackedDigits[0]) + String.valueOf(unpackedDigits[1]);
            }
        }
        if(decimalPointLocation > 0) {
            unpackedData = unpackedData.substring(0, (decimalPointLocation - 1)) + 
                            "." + 
                            unpackedData.substring(decimalPointLocation);
        }
        return unpackedData;
    }

    private byte[] unpackByte(byte packedData) {
        byte firstDigit = (byte) (packedData >>> 4);
        firstDigit = setBitsToZero(firstDigit, 4, 8);

        //System.out.println(" firstDigit = "+ firstDigit + ", and its bit string after unpacking = " + getBitString(firstDigit, 7));

        byte secondDigit = setBitsToZero(packedData, 4, 8);
        //System.out.println("second digit = " + secondDigit + ", and its bit string of second digit after unpcking = " + getBitString(secondDigit, 7));

        byte[] unpackedData = new byte[2];
        unpackedData[0] = firstDigit;
        unpackedData[1] = secondDigit;
        return unpackedData;
    }

    private byte setBitsToZero(byte number, int startBitPosition, int endBitPosition) {
        for (int i = startBitPosition; i < endBitPosition; i++) {
            number =  (byte) (number & ~(1 << i));
        }
        return number;
    }

Эта программа корректно работает со значениями целочисленного типа, но не работает со значениями типа с плавающей запятой.

Может ли кто-нибудь сказать, правильно ли моя программа?


person Shekhar    schedule 03.12.2013    source источник
comment
Вы проверили правильность последовательности цифр для нескольких тестовых случаев? Я не вижу, где вы добавляете символ «0» или 48 для преобразования в печатный символ. Вы уверены, что String.valueOf() возвращает символы «0»... «9» вместо целочисленных байтовых значений 0x00.. 0x09? Проблема при вставке десятичной точки в строку? Похоже, что decimalPointLocation 1 — это .######, 2 — это #.#####, 3 — это ##.#### и т. д. JUnit может быть полезен для проверки правильности работы функции unpackData для всех тестовых условий. Есть много угловых случаев, которые нужно проверить, даже не тестируя неправильно сформированные данные.   -  person MarkU    schedule 03.12.2013
comment
stackoverflow.com/questions/17448008/   -  person Bill Woodger    schedule 03.12.2013
comment
Плавающая точка — это не то же самое, что упакованное десятичное число.   -  person cschneid    schedule 03.12.2013
comment
Если какое-либо из этих упакованных полей подписано, вам придется иметь дело и с этим... Часто знак кодируется младшей значащей цифрой. Наконец, упакованные поля часто содержат подразумеваемую десятичную точку, вам потребуется исходное определение записи COBOL, чтобы отсортировать их.   -  person NealB    schedule 03.12.2013
comment
‹Педантичный режим› В середине packedData >>> 4 есть лишний знак >.   -  person ClickRick    schedule 17.09.2015


Ответы (3)


Данные COMP-3 (или «упакованные десятичные числа») выглядят так: 0x12345s, где «s» — это C для положительного значения, D для отрицательного или F для беззнакового. Таким образом, 0x12345c -> "12345", x012345d -> "-12345" и 0x12345f -> "12345".

У вас есть одна очевидная ошибка: вы игнорируете полубайт в байте, который содержит знаковый полубайт (например,, "5" выше), если знак отрицательный. Кроме того, вы слишком усердно работаете над манипулированием кусочками, это простой побитовый и или 4-битный сдвиг, чтобы изолировать кусочек.

Попробуйте что-то вроде этого (не проверено):

public String unpackData(String packedData, int decimalPointLocation) {
    String unpackedData = "";
    char[] characters = packedData.toCharArray();
    final int negativeSign = 13;
    for (int currentCharIndex = 0; currentCharIndex < characters.length; currentCharIndex++) {
        byte firstDigit = ((byte) characters[currentCharIndex]) >>> 4);
        byte secondDigit = ((byte) characters[currentCharIndex]) & 0x0F;
        unpackedData += String.valueOf(firstDigit);
        if (currentCharIndex == (characters.length - 1)) {
            if (secondDigit == negativeSign) {
                unpackedData = "-" + unpackedData;
            }
        } else {
            unpackedData += String.valueOf(secondDigit);
        }
    }
    if (decimalPointLocation > 0) {
        unpackedData = unpackedData.substring(0, (decimalPointLocation - 1)) + 
                        "." + 
                        unpackedData.substring(decimalPointLocation);
    }
    return unpackedData;
}
person Ross Patterson    schedule 03.12.2013
comment
Этот ответ, вероятно, будет работать большую часть времени, он ненадежен при чтении из файла. Чтение упакованного десятичного числа как строки символов может привести к повреждению упакованного десятичного числа. Вы должны читать и обрабатывать упакованное десятичное число как байты. - person Bruce Martin; 08.08.2017

В решении Росса Патерсона есть ошибка, когда первые 4 бита сдвигаются вправо. Маска 0x0F должна быть применена.

Вот исправленный метод:

private static String unpackData(byte[] packedData, int decimalPointLocation) {
    String unpackedData = "";

    final int negativeSign = 13;
    for (int currentCharIndex = 0; currentCharIndex < packedData.length; currentCharIndex++) {
        byte firstDigit = (byte) ((packedData[currentCharIndex] >>> 4) & 0x0F);
        byte secondDigit = (byte) (packedData[currentCharIndex] & 0x0F);
        unpackedData += String.valueOf(firstDigit);
        if (currentCharIndex == (packedData.length - 1)) {
            if (secondDigit == negativeSign) {
                unpackedData = "-" + unpackedData;
            }
        } else {
            unpackedData += String.valueOf(secondDigit);
        }
    }

    if (decimalPointLocation > 0) {
        int position = unpackedData.length() - decimalPointLocation;
        unpackedData = unpackedData.substring(0, position) + "." + unpackedData.substring(position);
    }
    return unpackedData;
}
person L.Bravo    schedule 09.06.2017

person    schedule
comment
Есть ли что-то особенное, что вы пытаетесь сообщить, что делает этот ответ лучше, чем предыдущий принятый? - person Matthew G.; 02.05.2014
comment
Я думаю, что это лучший ответ, потому что ввод представляет собой массив байтов. Чтение ebcdic comp-3 в символьном формате может повредить данные comp-3. - person Bruce Martin; 08.08.2017