7

C では次のようなレコードで構成されるバイナリ ファイルを読み込んでいます。

typedef _rec_t
{
  char text[20];
  unsigned char index[3];
} rec_t;

これで、これを解析して 23 の個別の値を持つタプルにすることができますnamedtupleが、最初の 20 バイトを に結合し、text残りの 3バイトを に結合するために使用できるとよいでしょうindex。どうすればそれを達成できますか?基本的に、23 個の値の 1 つのタプルの代わりに、それぞれ 20 個と 3 個の値の 2 つのタプルを持ち、「自然な名前」を使用してこれらにアクセスすることをお勧めしますnamedtuple

現在、 の形式"20c3B"を使用していますstruct.unpack_from()

注: を呼び出すと、文字列内に多数の連続したレコードがありますparse_text


私のコード(関連する部分に取り除かれます):

#!/usr/bin/env python
import sys
import os
import struct
from collections import namedtuple

def parse_text(data):
    fmt = "20c3B"
    l = len(data)
    sz = struct.calcsize(fmt)
    num = l/sz
    if not num:
        print "ERROR: no records found."
        return
    print "Size of record %d - number %d" % (sz, num)
    #rec = namedtuple('rec', 'text index')
    empty = struct.unpack_from(fmt, data)
    # Loop through elements
    # ...

def main():
    if len(sys.argv) < 2:
        print "ERROR: need to give file with texts as argument."
        sys.exit(1)
    s = os.path.getsize(sys.argv[1])
    f = open(sys.argv[1])
    try:
        data = f.read(s)
        parse_text(data)
    finally:
        f.close()

if __name__ == "__main__":
    main()
4

3 に答える 3

6

ドキュメントによると:http://docs.python.org/library/struct.html

解凍されたフィールドには、変数に割り当てるか、結果を名前付きタプルでラップすることで名前を付けることができます。

>>> record = 'raymond   \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record)

>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name='raymond   ', serialnum=4658, school=264, gradelevel=8)

だからあなたの場合

>>> import struct
>>> from collections import namedtuple
>>> data = "1"*23
>>> fmt = "20c3B"
>>> Rec = namedtuple('Rec', 'text index') 
>>> r = Rec._make([struct.unpack_from(fmt, data)[0:20], struct.unpack_from(fmt, data)[20:]])
>>> r
Rec(text=('1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'), index=(49, 49, 49))
>>>

アンパック変数のスライスが問題になる可能性があります。フォーマットがfmt = "20si"、またはシーケンシャルバイトを返さない標準である場合は、これを行う必要はありません。

>>> import struct
>>> from collections import namedtuple
>>> data = "1"*24
>>> fmt = "20si"
>>> Rec = namedtuple('Rec', 'text index') 
>>> r = Rec._make(struct.unpack_from(fmt, data))
>>> r
Rec(text='11111111111111111111', index=825307441)
>>>
于 2012-07-12T22:17:48.363 に答える
3

parse_textで文字列スライス(data [:20]、data [20:])を使用して2つの値を分解し、それぞれをstructで処理してみませんか?

または、23の値を取得して、2つに分割しますか?

私は何かが欠けているに違いありません。おそらく、これをstructモジュールを介して実現したいですか?

于 2012-07-12T21:55:49.957 に答える
3

これが私の答えです。@ samy.vilarstruct.unpack()は、「s」形式を使用して実際に文字列を取り出すことができると指摘しました。(覚えておけばよかった!)

この回答ではstruct.unpack()、文字列を取得するために 1 回、2 番目の文字列を整数として展開するために 1 回、2 回使用します。

このアイテムで何をしたいのか"3B"わかりませんが、それを 24 ビット整数としてアンパックしたいのでしょう。必要に応じて、3 文字の文字列の末尾に 0 バイトを追加し、整数としてアンパックしました。

ちょっとトリッキー: 次のような行はn, = struct.unpack(...)、長さ 1 のタプルを 1 つの変数にアンパックします。Python では、コンマがタプルを構成するため、1 つの名前の後に 1 つのコンマを使用して、長さ 1 のタプルを単一の変数にアンパックするためにタプル アンパックを使用しています。

また、a を使用してファイルを開くことができるため、ブロックwithが不要になります。ファイルのサイズを計算する必要なく、一度にファイル全体を読み取るためにtry使用することもできます。f.read()

def parse_text(data):
    fmt = "20s3s"
    l = len(data)
    sz = struct.calcsize(fmt)

    if l % sz != 0:
        print("ERROR: input data not a multiple of record size")

    num_records = l / sz
    if not num_records:
        print "ERROR: zero-length input file."
        return

    ofs = 0
    while ofs < l:
        s, x = struct.unpack(fmt, data[ofs:ofs+sz])
        # x is a length-3 string; we can append a 0 byte and unpack as a 32-bit integer
        n, = struct.unpack(">I", chr(0) + x) # unpack 24-bit Big Endian int
        ofs += sz
        ... # do something with s and with n or x

def main():
    if len(sys.argv) != 2:
        print("Usage: program_name <input_file_name>")
        sys.exit(1)

    _, in_fname = sys.argv

    with open(in_fname) as f:
        data = f.read()
        parse_text(data)

if __name__ == "__main__":
    main()
于 2012-07-12T22:34:01.167 に答える