はじめに
- intであれば、to_bytes, from_bytesを使ってもよい。
- ただしこの方法は、floatの場合は使えないなどのケースがあるため、structを使った方法を紹介する。
- 以降、数値はfloat 32bit little endianを例に記述する。
- 別の数値データを扱う場合は、
"<f"
の場所を必要に応じて変更すること。
import
import struct
float ⇔ bytes
structのpack/unpackで実現できる。
- float ⇒ bytes
val = 123.4
struct.pack('<f', val)
# Out: b'\xcd\xcc\xf6B'
- bytes ⇒ float
struct.unpack('<f', b'\xcd\xcc\xf6B')[0] # tuple型になるので0番目を取り出す。
# Out: 123.4000015258789
float配列 ⇔ bytes(要素数がわかっている場合)
"<ff"
と必要な要素数分f
を繰り返し書けば変換できる。
- float配列 ⇒ bytes
vals = [123.4, 234.5]
struct.pack('<ff', *vals)
# Out: b'\xcd\xcc\xf6B\x00\x80jC'
- bytes ⇒ float配列
list(struct.unpack('<ff', b'\xcd\xcc\xf6B\x00\x80jC')) # tuple型になるのでlistに変換
# Out: [123.4000015258789, 234.5]
float配列 ⇔ bytes(要素数が分かっていない場合)
- float配列 ⇒ bytes
structのpackにbyte連結のjoinを組み合わせる。
vals = [123.4, 234.5, 345.6, 456.7]
b''.join([struct.pack('<f', v) for v in vals])
# Out: b'\xcd\xcc\xf6B\x00\x80jC\xcd\xcc\xacC\x9aY\xe4C'
- bytes ⇒ float配列
structのiter_unpackで'<f'
のサイズ分ずつunpackできるので、これをlist内包表記と組み合わせる。
[i[0] for i in struct.iter_unpack('<f', b'\xcd\xcc\xf6B\x00\x80jC\xcd\xcc\xacC\x9aY\xe4C')]
# Out: [123.4000015258789, 234.5, 345.6000061035156, 456.70001220703125]
おまけ
- もし、以下のようにbytes配列をstrにしてファイル出力してしまった場合に元に戻したい。
vals = [123.4, 234.5, 345.6, 456.7]
bytes_data1 = b''.join([struct.pack('<f', v) for v in vals])
bytes_data1
# Out: b'\xcd\xcc\xf6B\x00\x80jC\xcd\xcc\xacC\x9aY\xe4C'
str_data = str(bytes_data1)
str_data
# Out: "b'\\xcd\\xcc\\xf6B\\x00\\x80jC\\xcd\\xcc\\xacC\\x9aY\\xe4C'" # これでファイルに書き込んでしまった!!汗
- 以下のような関数で逆変換ができる。
- 実行方法はコード内コメントを参照。
def str_to_bytes(str_data):
# 前後の"とbを削除してencodeする。
bytes_data = str_data.encode()[2:-2]
bytes_data = b""
for idx, _b in enumerate(str_data):
if _b == 92 or _b == 120: # \とxはスキップ
continue
else:
# \xの直後は、2文字で1byte情報となっている前半部分。ここで文字列を数値に変換する。
if str_data[idx-2] == 92 and str_data[idx-1] == 120:
a = str_data[idx]
b = str_data[idx+1]
# 一旦16新数文字列に変換し、整数にする
value = int("0x{}{}".format(chr(a), chr(b)), 0) # chrはアスキーコード(int)を文字に変換する
value = value.to_bytes(length=1, byteorder="little", signed=False)
bytes_data = bytes_data + value
# \xの2個後ろは、2文字で1byte情報となっている後半部分。変換済みなのでスキップ
elif str_data[idx-2] == 120 and str_data[idx-3] == 92: # 2
continue
# どちらでもない場合、1文字で1byteの情報となっているため、そのまま数値として読み込みbyte変換する。
else:
value = str_data[idx]
value = value.to_bytes(length=1, byteorder="little", signed=False)
bytes_data = bytes_data + value
return bytes_data
- この関数を使って元に戻すことができる。
vals = [123.4, 234.5, 345.6, 456.7]
bytes_data1 = b''.join([struct.pack('<f', v) for v in vals])
bytes_data1
# Out: b'\xcd\xcc\xf6B\x00\x80jC\xcd\xcc\xacC\x9aY\xe4C'
str_data = str(bytes_data1)
str_data
# Out: "b'\\xcd\\xcc\\xf6B\\x00\\x80jC\\xcd\\xcc\\xacC\\x9aY\\xe4C'"
bytes_data2 = str_to_bytes(str_data)
bytes_data2
# Out: b'\xcd\xcc\xf6B\x00\x80jC\xcd\xcc\xacC\x9aY\xe4C'