python-bitstring のドキュメントはすげー良くできてるからまとめ要らねんじゃね?
Python には bitstring という素敵 package があります。
GitHub - scott-griffiths/bitstring: A Python module to help you manage your bits
マニュアルもあるしこれまたすげー丁寧です。
GitHub - scott-griffiths/bitstring: A Python module to help you manage your bits
でも、加齢と共に学習能力が低下してるので書いて覚えるメソッド発動。Part I を適当に読み流すぜ!
>>> import bitstring
Chapter 3 Creation
bitstring って何かっつーと、bit 列を格納したり操作したりする為の package です。bit 列を格納してるクラスやインスタンスを作る関数が用意されています。
クラスが 4 つ用意されてるんですが、ベースになってるのが ConstBitArray でてんこ盛りにしたのが BitStream です。
pos 無し | pos 有り | |
---|---|---|
変更不可能 | ConstBitArray | ConstBitStream |
変更可能 | BitArray | BitStream |
- Const* だと __hash__ があるんで dict の key になれる
- filename など指定してファイルを読みこむ場合は Const* は最初からはファイル読みこまないけど Bit* の方は最初に全部ファイルを読みこもうとする
などの違いもあります。
bitstring の作り方
基本な作り方は 2 通りあって、引数で指定するか文字列で指定するか:
>>> bitstring.ConstBitArray(int=37, length=7) ConstBitArray('0b0100101') >>> bitstring.ConstBitArray('int:7=37') ConstBitArray('0b0100101')
どっちも signed int の 37 を表現する長さが 7 の bit 列が作られます。で、文字列だと並べたりもできて尚のこと素敵です。
>>> bitstring.ConstBitArray('int:4=1, int:4=2') ConstBitArray('0x12')
試しに int から作りましたが、他にもいろいろなものから作れます。manual から引用しちゃう:
auto | これがデフォルトの動作。string をよきに計らってくれます。'0x00' とか上の 'int:7=37' とか。'0x', '0o', '0b' ではじまるとそれぞれ 16 進, 8 進, 2 進 |
bytes | Python2 なら str, Python3 なら bytes をそのまま |
hex, oct, bin | '0xabc’, '0o123', '0b010101' などを解釈 |
int, uint | int と unsigned int を。bit length が必要。bit-wise big-endian |
intle, uintle | byte-wise little-endian。なんで length は 8 の倍数。 |
intbe, uintbe | byte-wise big-endian。なんで length は 8 の倍数。 |
intne, uintne | byte-wise native-endian。なんで length は 8 の倍数。 |
float / floatbe, floatle, floatne | 浮動小数を endian 指定で、length は 32 or 64 |
se, ue | Signed or unsigned exponential-Golomb coded integers、整数の encoding の一つね |
bool | True or False |
filename | path を指定してファイルを読む |
exponential-Golomb code に関しては付録が用意されてるので興味がある人はマニュアルを読もう!
つーわけで 37 はこんな感じで bitstring になります:
>>> bitstring.ConstBitArray(uintle=37, length=16) ConstBitArray('0x2500') >>> bitstring.ConstBitArray(uintbe=37, length=16) ConstBitArray('0x0025') >>> bitstring.ConstBitArray(ue=37) ConstBitArray('0b00000100110') >>> bitstring.ConstBitArray(float=37, length=32) ConstBitArray('0x42140000')
あとこの auto で解釈してくれるような文字列フォーマット (auto initialiser) は他の関数につっこんだりして、find でそれを検索したり == で比較するのにわざわざインスタンス作らなくても文字列との比較っぽく済ませられたり、中々重宝するのでしっかりマスターしましょう。
>>> a = bitstring.ConstBitArray('0b0101010101010101') >>> a.find('0b0') (0,) >>> a.find('0b1') (1,) >>> i = a.findall('0b1') >>> i.next() 1 >>> i.next() 3 >>> i.next() 5
>>> a=bitstring.ConstBitArray('0xabc') >>> a.oct '0o5274' >>> a == '0o5274' True >>> b = bitstring.ConstBitArray('0b010101') >>> b == 'int:6=21' True
便利!!
Chapter 4 Packing
これが真骨頂。
struct.pack の強化版みたいなのに bitstring.pack があって、format と材料を与えるとそこから BitStream を作ってくれます。
>>> bitstring.pack('int:7=10, bool=True') BitStream('0x15') >>> bitstring.pack('int:7, bool', 10, True) BitStream('0x15') >>> bitstring.pack('int:m=10, bool=n', m=7, n=True) BitStream('0x15')
なんか、すげー例がしょぼいですが
- format で完全に指定も
- format 用意しといて対応する実値を入れることも
- format で length や値を keyword にしといて後から keyword に値入れることも
できてすげー柔軟です。かっこいい。
struct.pack が好きな人は
>>> bitstring.pack('>5H', 1, 10, 100, 1000, 10000) BitStream('0x0001000a006403e82710')
なんてことできると知ると喜ぶに違いありません。
Chapter 5 Interpreting Bitstrings
bitstring は作ってしまったが最後単なる bit の列になります。単なる bit の列なんで、
>>> a=bitstring.ConstBitArray(bytes='abcd') >>> a ConstBitArray('0x61626364') >>> a.int 1633837924L >>> a.intle 1684234849 >>> a.float 2.6100787562286154e+20 >>> a.floatle 1.6777999408082104e+22
好きなように解釈できます。
似たようなのをもう 1 つ:
>>> a=bitstring.ConstBitArray(bytes='abcdef') >>> a.bin '0b011000010110001001100011011001000110010101100110' >>> a.oct '0o3026114331062546' >>> a.hex '0x616263646566' >>> a.bytes 'abcdef'
float は 32-bits or 64-bits でないといけなかったり、oct は (3 の倍数)-bits、hex は (4 の倍数)-bits でないと解釈できないのでエラーが出ます。
InterpretError: Cannot convert to octal unambiguously - not multiple of 3 bits. InterpretError: Cannot convert to hex unambiguously - not multiple of 4 bits. InterpretError: floats can only be 32 or 64 bits long, not 7 bits
みたいな、ね。
Chapter 6 Slicing, Dicing and Splicing
slice もできます:
>>> a[10:20] ConstBitArray('0b1000100110')
んー、でも bitstring っつーくらいだから普通に slice とると bit 列としての slice になっちゃうんです。が、8bit 単位 = byte 単位でと思うならば
>>> a[2:4:8] ConstBitArray('0x6364')
と、slice の step のところに 8 と入れてあげれば OK、このとき slice の index と 1 つめと 2 つめは単位が bit でなくて byte になります。
str っぽく扱えるので例えば結合できます、かけ算もできます。:
>>> bitstring.ConstBitArray('bytes=abc') + bitstring.BitStream('bytes=def') ConstBitArray('0x616263646566') >>> bitstring.ConstBitArray('bytes=abc') + 'bytes=def' ConstBitArray('0x616263646566') >>> 2*_ ConstBitArray('0x616263646566616263646566')
join と split もできます。
Bit* であれば
- append
- prepend
- slice で指定した範囲の del
- insert
- overwrite
- replace (Chapter 7 で出てくる)
もできます。これらに対しても引数に auto initialiser が使えます。
Chapter 7 Reading, Parsing and Unpacking
こっちこそ真骨頂?
read/peek
クラスが *Stream であれば、Stream と扱えるということは、読んでる位置である pos という変数を持っていて、read で好きな長さ読みだすことができます。read は読むと pos が進むけど、peek は進みません。
>>> f = bitstring.ConstBitStream(filename='/usr/share/dict/words') >>> f.pos 0 >>> f.read(64) ConstBitStream('0x0a410a4127730a41') >>> f.pos 64 >>> f.pos = 0 >>> f.read('int:64') 738883088816474689L >>> f.pos = 0 >>> f.read('float:64') 2.7706698372861208e-259 >>> f.pos = 0 >>> f.read('bytes:8') "\nA\nA's\nA" >>> f.pos = 0 >>> f.readlist(['bytes:1', 'int:8', 'int:16', 'float:32']) ['\n', 65, 2625, 3.3728583026705486e-15] >>> f.pos 64 >>> f.peek(64) ConstBitStream('0x4f4c0a414f4c2773') >>> f.pos 64 >>> f.peeklist(['int:32', 'folat:32']) [1330383425L, 3425137408.0] >>> f.pos 64
pos/bytepos
pos はもぉ見ましたが、8-bit を 1 step とする bytepos もあります。
>>> a = bitstring.ConstBitStream(bytes='abcdefghijklmn') >>> a.pos 0 >>> a.pos += 10 >>> a.pos 10 >>> a.bytepos ------------------------------------------------------------ Traceback (most recent call last): File "<ipython console>", line 1, in <module> File "/usr/local/lib/python2.6/site-packages/bitstring/constbitstream.py", line 86, in _getbytepos raise ByteAlignError("Not byte aligned in _getbytepos().") ByteAlignError: Not byte aligned in _getbytepos(). >>> a.pos += 6 >>> a.bytepos 2 >>> a.pos 16
pos が 8 の倍数でないとそりゃ bytepos で怒られます。
unpack
一方で unpack という method があってこれはみんなが使えます。pos が無くてもよくて、pos があってもいじりません。常に最初から読む感じです。
>>> f = bitstring.ConstBitArray(filename='/usr/share/dict/words') >>> f.unpack('8*bytes:10, int:32, 2*bool, 4*float:32') ["\nA\nA's\nAOL", "\nAOL's\nAac", 'hen\nAachen', "'s\nAaliyah", "\nAaliyah's", '\nAaron\nAar', "on's\nAbbas", '\nAbbasid\nA', 1650614643L, False, True, -2.5086945375745037e-16, 2.9651225187188671e-14, -0.10244781523942947, 1.2934015138606442e-35]
find/rfind/findall
bitstring の検索もできます。
- find は頭から探して最初の場所を
- rfind は後から探して最初の場所を
- findall は match する全ての場所を頭から答える generator を
返します。
>>> a = bitstring.ConstBitArray(bytes='abcdefghijklmn') >>> a.find('int:7=45') (70,) >>> a.rfind('int:7=45') (102,) >>> tuple(a.findall('int:7=45')) (70, 83, 102)
Chapter 8 Miscellany
細々した method の説明です。
- bytealign
- pos が 8 の倍数になるように、pos に range(7) から数字を足します。
- reverse
- bit 単位で順番をひっくり返します。破壊的。ひっくり返す範囲も指定できます。
- tobytes
- .bytes と同様ですが、length が 8 の倍数でない場合適当な数の '0b0' を append したものを返します。
- tofile
- mode が 'wb' で開かれてる file を引数にとって、保存します。tobytes 同様 padding します。
- startswith
- bool を返します。引数の auto initialiser で始まってるかどうかを返します。
- endswith
- startswith の終わってる番です。
- ror
- bit 単位で引数個だけ右に回します。破壊的。
- rol
- bit 単位で引数個だけ左に回します。破壊的。
あと、__ で始まる method の説明もあります。
感想
全然まとまってねーし、長ぇーし。