1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Python】 binary data を読み書き

Posted at

はじめに

少し前に、python でバイナリデータを読んでpacket をparse したり(PREAMBLEから読み始めてチェックサムで確認するとか)、データを読んだり(2byte を整数値にするとか)したりしました。そのとき、改めてバイナリデータの扱いを調べたので、メモ。何度も調べて、良く分かんらないまま使っていましたが、今は少しわかった気でいます。??

実際に仕事ではセンサーにコマンドを送ったり受け取ったメッセージを出コードするところを行うための実装でした。皆さんOSSを使うところを、おじさんはポチポチとコードを書いて読み解いています。

bytes型 (自分の理解)

pythonで定義されている bytes 型のデータを正しく使えば、何事もなく実装ができるはず、、です。

bytes型はどこからくるか

プログラム上で変数を作ることもできますが、自分の場合、ずばり、

  • binary type でfileを読んでいるとき
  • socket 通信しているとき
  • serial ポートからデータを読んでいるとき

です。そのとき、read/write しているのはこのbytes 型です。

In [  ]: with open("hoge.bin","rb") as f:
    ...:     buf = f.read(4)
    ...:     print(type(buf))
    ...:
<class 'bytes'>

整数値のリスト <--> bytes型

bytes 型と「整数値のリスト」は異なるものです。相互には変換可能です。(もちろん整数値は0から255に限られますが)

整数値

0から255までの整数値が1 byte を表すのはご存じのとおりです。なので、0から255までの整数値の系列(list 型)をbytes型に相互に変換できるのは分かり易いです。int()bytes() で cast するだけです。

In [15]: aaa = [1,2,3,4,5,255]
In [16]: bytes(aaa)
Out[16]: b'\x01\x02\x03\x04\x05\xff'
In [25]: list(bytes(aaa))
Out[25]: [1, 2, 3, 4, 5, 255]

ただし、整数値は0から255の間の整数値に限られます。

In []: aaa = [1,2,3,4,5,256]
In []: bytes(aaa)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-3b250851e608> in <module>
----> 1 bytes(aaa)

ValueError: bytes must be in range(0, 256)

16進数で表記

0xff と書くとint 型になります。bytes 型にするには b'\xffと書きます。

bytes型の変数は、リストで参照するように [ ] でインデックスを指定して参照することができます。

In []: b1 = b'\xaabbcc'
In []: type(b1)
Out[]: bytes
In []: b1[0]
Out[]: 170
In []: hex(b1[0])
Out[]: '0xaa'

以下のように書いたら、変数で整数値になります。

In [15]: i1 = 0xaabbcc
In [16]: type(i1)
Out[16]: int
In [17]: i1
Out[17]: 11189196

当然、これを [ ] で参照することはできません。

In [18]: i1[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-9726669fc462> in <module>
----> 1 i1[0]

TypeError: 'int' object is not subscriptable

これをbytes 型にするには、to_bytes で変換する必要があります。endian を指定します。

In [21]: i1.to_bytes(3, 'little')
Out[21]: b'\xcc\xbb\xaa'
In [22]: i1.to_bytes(3, 'big')
Out[22]: b'\xaa\xbb\xcc'
In [27]: i1.to_bytes(3,'big')[0]
Out[27]: 170

bytes 型とstr型

これはdecode/encode するだけです。文字コードは必要な時に指定します。

In [31]: "こんにちは".encode()
Out[31]: b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf'
In [32]: b2 = "こんにちは".encode()
In [33]: b2.decode()
Out[33]: 'こんにちは'

int型とstr型(2進数、16進数表記)

話は前後しますが、整数値は2進数や16進数表記の文字列に変換できます。

In [37]: hex(100)
Out[37]: '0x64'
In [38]: bin(100)
Out[38]: '0b1100100'

一方で、コードに直接書けばint 型になります。

In [39]: i3 = 0x64
In [40]: i3
Out[40]: 100
In [41]: i4 = 0b1100100
In [42]: i4
Out[42]: 100

import struct

そして、最後に、float を含めて連続していろいろつまっているbytes には unpack で全てdecode させてしまう、という、とても便利な関数unpack が用意されています。素直に、docs のexample を読むのが分かり易いです。

このunpack のおかげで、かなりコードの長さが短くなった気がします。

まとめ

bytes型で入ってくるデータは、適切な処理をすることで所望の読み方をすることができた。
あと少ししたら、どうせすぐ忘れてしまうので、メモしました。TODOは今のところ無しかな。
(2021/06/30)

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?