記事内にはプロモーションが含まれています

Pythonでバイナリファイルを読み込む方法!基本から変換・解析まで徹底解説

Pythonでバイナリファイルを読み込む方法!基本から変換・解析まで徹底解説 プログラミングの疑問解決

Pythonを使用して開発を行っていると、テキストファイルだけでなく、画像や音声、あるいは独自のフォーマットで保存された「バイナリファイル」を扱わなければならない場面に遭遇します。

テキストエディタで開いても文字化けして読めないこのデータを、どのようにプログラムで読み込み、解析すればよいのでしょうか。

この記事では、Pythonにおけるバイナリファイルの読み込み方法について、基本となるopen関数の使い方から、読み込んだバイトデータの数値変換、structモジュールを使った高度な解析まで、初心者にもわかりやすく解説していきますので、是非参考にしてください。

【著者プロフィール&本記事の信頼性】
プロフィール
  • 著者は元エンジニア
  • 大手プログラミングスクールのWebディレクター兼 ライターを経験
  • 自らも地元密着型のプログラミングスクールを運営
プロフィール詳細はコチラ
当ブログ著者
当ブログ著者
忖度は一切なし!
本気で未経験からエンジニア転職を目指すなら、
日本最大級の比較サイト「マイベスト」で【4年連続人気NO.1】となった
RUNTEQ(ランテック)一択!
【RUNTEQの特徴】
✅受講生からの評判が驚くほど良い
✅学習はハードだが未経験とは思えないほど高いスキルが身に付く
挫折させない万全なサポート体制が用意されている
✅採用面接で担当者に刺さるレベルの高い「ポートフォリオ」を作成できる
✅給付金を使えば実質約13万円という格安料金で受講できる

\ もちろん勧誘行為は一切なし! 相談だけでもOK! /

Pythonにおけるバイナリファイルの基本概念

コンピュータ上のファイルは、大きく分けて「テキストファイル」と「バイナリファイル」の2種類に分類されます。

Pythonでこれらを扱う際には、明確な区別が必要です。

まずは、バイナリファイルがどのようなものか、そしてPythonで扱う際の基本的なルールについて理解を深めましょう。

バイナリファイルとは何か

バイナリファイルとは、文字コード(UTF-8やShift_JISなど)に基づいてエンコーディングされていない、コンピュータが直接処理するための形式で保存されたファイルのことです。

具体的には、以下のようなものがバイナリファイルとなります。

  • 画像ファイル(JPEG、PNG)
  • 音声ファイル(MP3、WAV)
  • 実行ファイル(EXE)
  • 特定のアプリケーション専用のデータファイル

テキストファイルは、人間が読んで理解できる文字で構成されていますが、バイナリファイルは「0と1のビット列(バイトデータ)」で構成されています。

そのため、通常のエディタで無理やり開こうとすると、意味不明な記号の羅列として表示されてしまいます。

Pythonでこれらのファイルを操作する場合、テキストとしてではなく「バイト列(bytes)」として読み込む必要があります。

テキストモードとバイナリモードの違い

Pythonの標準機能であるopen関数を使ってファイルを開く際、モードの指定が非常に重要になります。

デフォルトのモードは「テキストモード(t)」であり、これは文字列(str型)としてファイルを読み書きします。
一方、バイナリファイルを扱う場合は「バイナリモード(b)」を指定しなければなりません。

もしバイナリファイルをテキストモードで開いてしまうと、Pythonはデータを特定の文字コードで解釈しようとし、デコードエラーが発生したり、改行コードの自動変換によってデータが破損したりする可能性があります。

そのため、画像や音声、数値データなどを扱う際は、必ずバイナリモードを明示的に指定してファイルを開くことが鉄則です。

バイナリファイルを読み込む基本的な方法

それでは、実際にPythonコードを使ってバイナリファイルを読み込む手順を見ていきましょう。

ここでは、ファイル全体を一括で読み込む方法と、少しずつ読み込む方法の2パターンを紹介します。

基本となるのは「open関数」と「モード指定」です。

open関数とモード「rb」の使い方

バイナリファイルを読み込むには、open関数の第二引数(mode)に rb を指定します。
r は「読み込み(Read)」、b は「バイナリ(Binary)」を意味しています。

また、ファイル操作には with 構文を使用するのが一般的です。
これにより、処理が終わった後に自動的にファイルが閉じられ、リソースの解放漏れを防ぐことができます。

以下は、ファイル全体を一度に読み込む基本的なサンプルコードです。

# バイナリファイルの読み込みサンプル

file_path = 'sample.bin'

# サンプル用のバイナリファイルを作成(実行確認用)
with open(file_path, 'wb') as f:
    f.write(b'\x00\x01\x02\x03\x48\x65\x6c\x6c\x6f')

# バイナリモード 'rb' でファイルを読み込む
with open(file_path, 'rb') as f:
    data = f.read()
    
    # 読み込んだデータの型と内容を表示
    print(f'Type: {type(data)}')
    print(f'Data: {data}')

上記のコードを実行すると、以下のような結果が得られます。

Type: <class 'bytes'>
Data: b'\x00\x01\x02\x03Hello'

このコードでは、まず書き込みモード(wb)でサンプルのバイナリファイルを作成しています。

その後、rb モードで開き直し、f.read() メソッドで中身をすべて読み出しました。

結果を見ると、データの型は bytes(バイト列)になっており、中身がバイトデータとして取得できていることがわかります。

b’\x…’ という表記は、16進数表現のバイト値を表しています。

readメソッドでバイト数を指定して読み込む

ファイルサイズが巨大な場合、全体を一度に読み込むとメモリを圧迫する恐れがあります。

また、ファイルの先頭から「4byteはヘッダー、次の2byteはID」のように、決まったバイト数ごとに処理したいケースも多々あります。

そのような場合は、read() メソッドの引数に読み込みたいバイト数を整数で指定します。

file_path = 'sample.bin'

# 実行確認用にファイルを再作成
with open(file_path, 'wb') as f:
    f.write(b'\x10\x20\x30\x40\x50\x60\x70\x80')

with open(file_path, 'rb') as f:
    # 最初の3バイトを読み込む
    chunk1 = f.read(3)
    print(f'Chunk 1: {chunk1}')
    
    # 続きから2バイトを読み込む
    chunk2 = f.read(2)
    print(f'Chunk 2: {chunk2}')
    
    # 残りをすべて読み込む
    chunk3 = f.read()
    print(f'Chunk 3: {chunk3}')

実行結果は以下のようになります。

Chunk 1: b'\x10\x20\x30'
Chunk 2: b'\x40\x50'
Chunk 3: b'\x60\x70\x80'

read() メソッドは、呼び出すたびにファイル内の読み込み位置(ファイルポインタ)を自動的に進めます。
そのため、連続して呼び出すだけで、データを順番に取得していくことが可能です。

これを利用すれば、巨大なファイルでも少しずつメモリに展開しながら処理を進められます。

読み込んだバイナリデータを数値や文字列に変換する

open関数で読み込んだデータは bytes型です。

このままでは単なるバイトの羅列にすぎないため、プログラムで活用するには整数(int)や文字列(str)などに変換する必要があります。

ここでは、頻出する変換テクニックを紹介します。

int.from_bytesで整数に変換する(エンディアン指定)

バイナリデータとして保存された数値をPythonの整数型として扱うには、int.from_bytes() メソッドを使用します。

この際、重要なのが「エンディアン(バイトオーダー)」の指定です。

データが「ビッグエンディアン(上位バイトから順に並ぶ)」か「リトルエンディアン(下位バイトから順に並ぶ)」かによって、変換結果が全く異なります。

# 2バイトのデータ
bytes_data = b'\x01\x02'

# ビッグエンディアンとして変換 (0x0102 = 258)
val_big = int.from_bytes(bytes_data, 'big')
print(f'Big Endian: {val_big}')

# リトルエンディアンとして変換 (0x0201 = 513)
val_little = int.from_bytes(bytes_data, 'little')
print(f'Little Endian: {val_little}')

実行結果は以下の通りです。

Big Endian: 258
Little Endian: 513

ネットワーク通信のデータはビッグエンディアン、Intel系のCPUやWindowsのファイルフォーマットなどはリトルエンディアンが採用される傾向にあります。

扱うファイルの仕様書を確認し、適切なエンディアンを指定しましょう。

decodeメソッドで文字列に変換する

バイナリデータの中にテキスト情報が含まれている場合は、decode() メソッドを使って文字列型に変換します。

このとき、どの文字コードで保存されているかを指定する必要があります。(UTF-8、ASCII、Shift_JISなど)

# UTF-8でエンコードされた日本語のバイト列
text_bytes = b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf'

# 文字列にデコード
text_str = text_bytes.decode('utf-8')
print(f'Decoded: {text_str}')

実行結果は以下の通りです。

Decoded: こんにちは

適切な文字コードを指定しないと、UnicodeDecodeError が発生したり、文字化けしたりするため注意が必要です。

structモジュールを使った高度なバイナリ解析

複数のデータ型(整数、浮動小数点数、文字列など)が混在しているバイナリデータを読み込む場合、手動でバイトを切り出して変換するのは手間がかかります。

Pythonの標準ライブラリである struct モジュールを使用すると、C言語の構造体のような感覚で、フォーマットを指定して一括でデータを抽出(アンパック)できます。

struct.unpackによるデータ抽出

struct.unpack() 関数を使用すると、フォーマット文字列に従ってバイト列をタプル形式のデータに変換できます。

フォーマット文字列では、型(整数、short、floatなど)やエンディアンを指定します。

import struct

# サンプルデータ作成
# 整数(4byte) + 浮動小数点(8byte) + 文字(3byte)
# > はビッグエンディアン、Iはunsigned int、dはdouble、3sは3バイトの文字
sample_data = struct.pack('>Id3s', 12345, 3.14159, b'ABC')

# structを使ってアンパック(解析)
# フォーマット '>Id3s' の意味:
# > : ビッグエンディアン
# I : 符号なし整数 (4バイト)
# d : 倍精度浮動小数点数 (8バイト)
# 3s: 3バイトの文字列
unpacked_data = struct.unpack('>Id3s', sample_data)

print(f'Raw Data: {sample_data}')
print(f'Unpacked: {unpacked_data}')
print(f'Integer: {unpacked_data[0]}')
print(f'Float: {unpacked_data[1]}')
print(f'String: {unpacked_data[2].decode()}')

実行結果は以下の通りです。

Raw Data: b'\x00\x0009@\t!\xf9\xf0\x1b\x86nABC'
Unpacked: (12345, 3.14159, b'ABC')
Integer: 12345
Float: 3.14159
String: ABC

この例では、バイナリデータを一度に読み込み、整数・浮動小数点数・バイト文字の3つの要素に分解しています。

独自フォーマットのファイルや、センサーからのバイナリログなどを解析する際に非常に強力なツールとなります。

配列としてバイナリデータを扱う方法

画像データや音声波形のように、同じ型の数値が大量に並んでいるバイナリデータを扱う場合、リストやNumPy配列として読み込むと、その後の計算や加工が容易になります。

list関数でのリスト変換

単純にバイト列を整数のリスト(0〜255の値の配列)に変換したい場合は、組み込みの list() 関数を使うのが最も簡単です。

bytes_data = b'\x00\xFF\x10\x20'

# バイト列を整数のリストに変換
int_list = list(bytes_data)

print(f'List: {int_list}')

実行結果は以下の通りです。

List: [0, 255, 16, 32]

これだけで、各バイトの値が要素となったリストが得られます。データの加工や分析を行うための第一歩として便利です。

arrayモジュールやNumPyの活用

より効率的に、あるいは2byte以上の数値を配列として扱いたい場合は、標準ライブラリの array モジュールや、外部ライブラリのNumPyを利用します。

特にデータ分析や画像処理を行う場合、NumPyのfromfile()やfrombuffer()は高速で非常に有用です。

import numpy as np

file_path = 'data.bin'

# テストデータの準備(16bit整数を4つ)
with open(file_path, 'wb') as f:
    f.write(b'\x01\x00\x02\x00\x03\x00\x04\x00') # リトルエンディアンで1, 2, 3, 4

# NumPyを使ってint16型の配列として読み込む
# dtype='<i2' は リトルエンディアン(<)の2バイト整数(i2) を意味する
np_array = np.fromfile(file_path, dtype='<i2')

print(f'NumPy Array: {np_array}')

実行結果は以下の通りです。

NumPy Array: [1 2 3 4]

大量のバイナリデータを高速に読み込み、そのまま行列計算などに利用できるため、科学技術計算の分野では必須のテクニックといえます。

初心者がPythonについて効率的に学ぶには

Pythonでバイナリデータを読み込む方法などをはじめとするPythonの知識・スキルを効率的に習得するには、プログラミングスクールの活用が最も近道です。

スクールでスキルを高めることにより、今の仕事に活かしたり、副業として高単価な案件を受注できたりするだけでなく、Pythonエンジニアとして転職することも可能になります。

Pythonエンジニアは需要が非常に高いため、それに比例して年収も高くなる傾向にあります。
「今よりも年収を上げたい」「将来性の高い職種であるエンジニアへ転職したい」といった気持ちが強い場合は、プログラミングスクールでPythonの専門スキルを習得しつつ、ポートフォリオ支援や転職支援を受けてエンジニアへ転職する、という道を目指すのもよいでしょう。

なお、「Pythonに強く、受講生からの評判も良いプログラミングスクール」には、以下のようなところがあります。

その他、以下の記事でもPythonのおすすめスクールをまとめていますので、興味のある方は是非参考にしてください。