【Python】struct.unpackとunpack_fromの使い方:バイナリデータを効率よく復元する方法

この記事は約4分で読めます。

Pythonでバイナリデータを扱う際、前回の記事で紹介した「struct.pack」でデータをバイト列に変換したあと、必ず必要になるのがデータの復元(デコード)です。

この記事では、バイト列をPythonの数値や文字列に戻す「struct.unpack」と、メモリ効率の良い「struct.unpack_from」の使い方を、初心者にもわかりやすく解説します。

1. struct.unpackとは?(packの逆操作)

struct.unpackは、C言語の構造体のようなバイナリデータ(バイト列)を、Pythonのタプルに変換するための関数です。

前回の記事(struct.packの使い方)で作成したバイナリデータを、元の数値や文字列に戻す際に使用します。

基本的な書き方

import struct

# 例:int(4バイト)とfloat(4バイト)のバイナリデータ
binary_data = b'\x01\x00\x00\x00\x00\x00\x80\x3f'

# unpack(フォーマット, データ)
result = struct.unpack('<if', binary_data)

print(result)
# 出力: (1, 1.0)

ポイント:

  • 戻り値は常に「タプル」になります。要素が1つだけでも (値,) という形で返ってきます。
  • 第1引数のフォーマット文字列は、packした時と全く同じにする必要があります。

2. エンディアンとフォーマット文字の復習

unpackを正しく行うためには、データの並び順(エンディアン)を指定することが重要です。

文字意味
<リトルエンディアン(Windowsなど一般的に使用)
>ビッグエンディアン(ネットワーク通信など)
i4バイト整数 (int)
f4バイト浮動小数点 (float)
s文字列 (char[])

※詳細はstruct.packの記事を参照してください。

3. 大容量データに最適!struct.unpack_fromの使い方

通常のunpackは、データ全体のサイズとフォーマットのサイズが一致していないとエラー(struct.error)になります。

しかし、「長いバイナリデータの中の、特定の場所(オフセット)からデータを抜き出したい」という場面も多いはず。そんな時に便利なのが struct.unpack_from です。

unpack_fromのメリット

  • スライス(data[4:8]など)を使わずに済むため、余計なメモリコピーが発生せず高速。
  • 「○バイト目から読み込む」という指定が簡単。

使用例:特定のオフセットから読み込む

import struct

# 12バイトのバイナリデータ(4バイトずつ int, int, int)
data = struct.pack('<iii', 10, 20, 30)

# 4バイト目(2番目のデータ)から1つ(int)だけ読み込みたい場合
# unpack_from(フォーマット, データ, オフセット)
result = struct.unpack_from('<i', data, 4)

print(result)
# 出力: (20,)

4. よくあるエラーと対処法

error: unpack requires a buffer of 4 bytes

このエラーは、「フォーマットで指定したサイズ」と「実際のバイナリデータのサイズ」が合っていない時に発生します。

  • 原因1: unpackを使っているのに、データの一部だけを渡していない。
  • 解決策: データの一部を抜き出したいなら unpack_from を使うか、スライスでサイズを合わせてください。

期待した数値にならない

  • 原因: エンディアン(<>)の指定が間違っている可能性があります。
  • 解決策: 書き込み側(pack)と読み込み側(unpack)でエンディアンの設定を統一しましょう。

5. まとめ

  • struct.unpack:バイナリデータをPythonの型に変換する(戻り値はタプル)。
  • struct.unpack_from:データの途中の位置から効率よく読み込む。
  • 注意点:必ずpack時と同じフォーマット文字列・エンディアンを使用する。

バイナリデータの扱いに慣れると、独自のファイルフォーマットの解析や、ハードウェアとの通信プログラムが書けるようになります。

まずはstruct.packでデータを作り、それをunpackで戻す練習から始めてみてください!

コメント

タイトルとURLをコピーしました