Декодирование упакованных полей COMP-3 в файл ASCII на Python?
У меня есть файл, который раньше был файлом в кодировке EBCDIC, который был преобразован в ASCII с использованием dd. Однако некоторые строки содержат упакованные в COMP-3 поля, которые я хотел бы прочитать.
Например, строковое представление одной из строк, которые я хотел бы декодировать:
'15\x00\x00\x00\x04@\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x0c777093020141204NNNNNNNNYNNNN\n'
Поле, которое я хотел бы прочитать, определяется как PIC S9(09) COMP-3 POS. 3
то есть поле, которое начинается с третьего байта и имеет длину девять байтов при декодировании (и, следовательно, пять байтов при кодировании в соответствии со спецификацией COMP-3).
Я понимаю спецификацию COMP-3 и знаю, что для этой конкретной строки целочисленное значение этого поля должно быть 315
, но я не могу понять, что делать, чтобы фактически декодировать поле. Я также не уверен, что тот факт, что файл был преобразован с dd
в ASCII это проблема здесь или нет.
Кто-нибудь работал над подобной проблемой раньше, или я что-то упускаю из виду? Спасибо!
2 ответа
Да, проблема в том, что файл содержит не символьные данные и был преобразован из EBCDIC в ASCII на уровне файла или записи. Это не проблема, какой инструмент был использован для этого.
Безусловно, самая простая вещь для вас - это запросить данные только в виде символов. Если данные содержат подписанные поля, знак должен быть отдельным, а там, где имеются подразумеваемые десятичные разряды, они должны быть действительными или указываться масштабным значением (в зависимости от того, что вам удобнее).
Тогда вам не нужно ничего конвертировать. Я никогда не могу понять, как люди думают, что они могут просто дать вам данные EBCDIC, содержащие "что угодно", и ожидать, что вы разберетесь с ними.
Если вы щелкнете по метке EBCDIC, вы найдете другие решения, которые вы сможете применить, если по какой-то идиотской причине данные персонажа не будут доступны из источника EBCDIC. Так как они уже дали тебе дерьмо, они могут придумать какую-то идиотскую причину. Если это так, документируйте это (вежливо) своему боссу.
Если вы получаете символьные данные, то вы можете преобразовать их в dd или что-либо еще (если вы все еще получаете забавные вещи, проверьте кодовые страницы).
Причина, по которой все становится запутанным, если вы преобразуете не символьные данные, иллюстрируется этим:
05 a-packed-decimal-positive-five COMP-3 PIC S9 VALUE +5.
05 a-character-asterisk PIC X VALUE "*".
Оба из них, в EBCDIC, имеют шестнадцатеричное значение 5C
, Оба будут преобразованы в ASCII-звездочку. Значение COMP-3, равное пяти, было потеряно. Обратите внимание, что COMP-3 может, вне знака младшего разряда, принимать любую пару числовых цифр для каждого из своих байтов. Рассол, когда вы попали в контрольного персонажа. То же самое для "бинарных" полей, еще хуже, потому что больше возможностей случайного попадания.
Если обратное преобразование кодировки символов должно было быть выполнено, то значение может быть в состоянии быть определенным; поскольку есть [веские основания для этого] сомневаться, лучше всего сделать так, как предложил Билл Вуджер, и получить новую копию данных в текстовом формате или получить новую копию исходных данных, но не повредить данные с символьным переводом по своей природе двоичных [частей] данных. Я уверен, что в этом конкретном случае это значение можно определить; но как 0d377 (+377), а не 0d315 (+315).
Надеюсь, смысл может быть в следующем:
ASCII-строка (заданная \ xEncoded):
'15\x00\x00\x00\x04@\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x0c777093020141204NNNNNNNNYNNNN\n'
ASCII (hex):
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8....+....9....+
X'31350000000440000000000C000000000C3737373039333032303134313230344E4E4E4E4E4E4E4E594E4E4E4E0A'
-04- ASCII x04->x37 in EBCDIC [control character End of Transmission (EOT)]
-40- ASCII x40->x7C in EBCDIC [or xB5 or x80 or xEC or ?? per @ is a variant character in EBCDIC]
EBCDIC:
....+....1....+....2....+....3....+....4....+....5....+....6....+....7....+....8....+....9....+
x'F1F5000000377C000000000C000000000CF7F7F7F0F9F3F0F2F0F1F4F1F2F0F4D5D5D5D5D5D5D5D5E8D5D5D5D525'
-37- EBCDIC x37->x04 in ASCII [control character End of Transmission (EOT)]
-7C- EBCDIC x7C->x40 in ASCII [or A7 or 25 or ?? per x7C does not represent an invariant character in EBCDIC]
Байты данных в PIC S9(09) COMP-3 POS. 3
это десятичное число в двоичном формате (BCD) для пяти байтов с позиций от пяти до четырнадцати [в показанных линиях шкалы; десять шестнадцатеричных цифр 000000377C
], представляет положительное десятичное целое значение 377
, Я мало сомневаюсь, что это была первоначальная стоимость.
Случайно, преобразование из EBCDIC в ASCII для этой конкретной строки не было повреждено из-за невозможности обратного преобразования символов. Следующие два значения в записи также предположительно определены одинаково, и на них тоже не влияет потеря данных при преобразовании как в EBCDIC, так и из него; т.е. управляющий символ с кодовой точкой x0C одинаков как в EBCDIC, так и в ASCII, и оба имеют десятичное значение положительного нуля.
В то время как, возможно, была другая возможная кодовая страница, из которой можно было бы совершить поездку в оба конца, CP00037 обеспечил сильного соперника [с x7C с действительным знаковым откусыванием] и действительным преобразованием; значение 315
кажется маловероятным, поскольку зарезервированный управляющий символ EBCDIC x31 должен был бы быть переведен в ASCII x04 вместо x91 или xBA, а наиболее вероятный EBCDIC x5C необъяснимым образом должен был бы быть преобразован в ASCII x40 вместо x2A [или как отрицательное значение x5D необъяснимым образом преобразуется в ASCII x40 вместо x29; не рассматривались какие-либо предпочтительные возможности вывесок], ни один из которых не имеет никакого смысла.
Я заметил, что при большом количестве проб и ошибок прямое кодирование в формат Ascii приведет к правильному числу, за исключением последней цифры и знака. Для перевода последней цифры есть таблица преобразования. Вот что я сделал с быстрым и грязным кодом, который подходит для моего варианта использования. Мой файл загружен во фрейм данных в пандах, и я вызываю эту функцию, чтобы выполнить перевод за меня, передав значение и количество десятичных знаков.
sign = {'{': 1,'A': 1,'B': 1,'C': 1,'D': 1,'E': 1,'F': 1,'G': 1,'H': 1,'I': 1,'}': -1,'J': -1,'K': -1,
'L': -1,'M': -1,'N': -1,'O': -1,'P': -1,'Q': -1,'R': -1 }
last_digit = {'{': 0,'A': 1,'B': 2,'C': 3,'D': 4,'E': 5,'F': 6,'G': 7,'H': 8,'I': 9,'}': 0,'J': 1,'K': 2,
'L': 3,'M': 4,'N': 5,'O': 6,'P': 7,'Q': 8,'R': 9 }
def unpack(value,decimal):
l = value.str[-1:]
s = l.map(sign)
d = l.map(last_digit)
num = value.str[:-1]
return (num.apply(int)*10+d)*s/10**decimal
Теперь ваше новое поле в фрейме данных может быть:
df['unpacked'] = unpack(df['Packed'],2)