在Python中解码ASCII文件中的COMP-3压缩字段



我有一个文件,以前是EBCDIC编码的文件,使用dd将其转换为ASCII。但是,有些行包含我想要读取的COMP-3压缩字段。

例如,我想解码的一行的字符串表示为:

'15x00x00x00x04@x00x00x00x00x0cx00x00x00x00x0c777093020141204NNNNNNNNYNNNNn'

我想读取的字段由PIC S9(09) COMP-3 POS. 3指定,也就是说,从第三个字节开始的字段,解码时长9个字节(因此,根据COMP-3规范,编码时长5个字节)。

我理解COMP-3规范,也知道对于这条特定的行,该字段的整数值应该是315,但我不知道该怎么做才能真正解码该字段。我也不确定用dd将文件转换为ASCII这一事实是否存在问题。

以前有人处理过类似的问题吗,或者我明显遗漏了什么?非常感谢。

是的,文件包含非字符数据,并且已在文件或记录级别从EBCDIC转换为ASCII,这是一个问题。用什么工具来做这件事不是问题。

到目前为止,对你来说最简单的事情就是要求只以字符的形式向你提供数据。如果数据包含带符号的字段,则符号应是单独的,如果有隐含的小数位数,则这些小数位数应为实际值,或由缩放值表示(以您更方便的为准)。

那么你什么都不需要转换。我永远无法理解人们是如何认为他们可以给你包含"任何"的EBCDIC数据,并期望你对其进行排序的。

如果您单击EBCDIC标记,您将找到一些其他解决方案,如果由于某些愚蠢的原因,无法从EBCDIC源获得字符数据,则您可以应用这些解决方案。既然他们已经给了你废话,他们可能会想出一些愚蠢的理由。如果是,(礼貌地)把它记录给你的老板。

如果你得到了字符数据,那么你可以添加或其他任何东西来转换它(如果你仍然得到了看起来很有趣的东西,请查看代码页)。

如果你转换非字符数据,事情会变得棘手的原因如下:

05  a-packed-decimal-positive-five COMP-3 PIC S9 VALUE +5.
05  a-character-asterisk PIC X VALUE "*".

在EBCDIC中,这两个都具有十六进制值5C。两者都将转换为ASCII星号。COMP-3值5已经丢失。注意,COMP-3可以在低位符号之外,为其每个字节取任意一对数字。当你碰巧碰到一个控制角色时,Pickle。"二进制"字段也是如此,更糟糕的是,意外命中的可能性更大。

如果要执行反向字符编码转换,则可以确定值;因为有[充分的理由]对此表示怀疑,最好的做法是按照Bill Woodger的建议,以文本格式获取新的数据副本,或者获取原始数据的新副本,但不要用固有二进制[部分]数据的字符翻译破坏数据。在这种特定情况下,我相信该值是可确定的;但是作为0d377(+377)而不是0d315(+315)。
希望以下内容能有意义:

ASCII字符串(给定\xEncoded):

'15x00x00x00x04@x00x00x00x00x0cx00x00x00x00x0c777093020141204NNNNNNNNYNNNNn'

ASCII(十六进制):

  ....+....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),从位置5到14的五个字节[在所示的刻度线中;十个十六进制数字000000377C],表示正十进制整数值377。我毫不怀疑,这就是最初的价值。

碰巧的是,对于该特定字符串,从EBCDIC到ASCII的转换没有因为无法进行往返字符转换而损坏。记录中的下两个值也可能被定义为相同的,并且在与EBCDIC的转换中,这些值也不受数据丢失的影响;即代码点为x0C的控制字符在EBCDIC和ASCII中都是相同的,并且都具有正零的十进制值。

虽然可能还有其他可能的代码页可以尝试往返,但CP00037提供了一个强有力的竞争者[带有有效符号半字节的x7C]和有效的转换;CCD_ 8的值似乎非常不可能,因为保留的EBCDIC控制字符x31将不得不转换成ASCII x04而不是x91或xBA,并且最有可能的EBCDIC x5C莫名其妙地必须转换为ASCII x40而不是x2A[或者作为负值x5D莫名其妙地转换为ASCII x40而不是x29;没有考虑任何非优选的标牌可能性],这两者都没有任何意义。

经过大量的试错,我注意到,直接编码成Ascii格式会得到正确的数字,除了最后一个数字和符号。有一个转换表来翻译最后一个位数。以下是我对适用于我的用例的一些快速而肮脏的代码所做的操作。我的文件被加载到pandas中的一个数据帧中,我调用这个函数通过传递值和小数位数来为我进行翻译。

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)

最新更新