Pythonでファイルの読み書きやネットワーク通信を扱っていると、バイナリデータ(bytes型)を文字列(str型)に変換したい場面が出てきます。画像ファイルの処理、APIのレスポンス解析、テキストファイルのエンコード変換など、バイナリと文字列の相互変換はPythonプログラミングの重要なスキルのひとつです。
Pythonではbytes型のdecode()メソッドを使ってバイナリを文字列に変換し、str型のencode()メソッドで文字列をバイナリに変換できます。UTF-8・Shift-JIS・Base64など、エンコード方式の理解も合わせて知っておくと、様々なデータ変換に対応できるようになります。
この記事では、Pythonでバイナリを文字列に変換する方法を、decode()の基本・エンコード指定・Base64変換・エラー処理まで、サンプルコードとともにわかりやすく解説していきます。
Pythonでバイナリを文字列に変換するにはdecode()メソッドが基本
それではまず、Pythonでバイナリ(bytes型)を文字列(str型)に変換する基本的な方法について解説していきます。
Pythonでバイナリを文字列に変換するには、bytes型オブジェクトのdecode()メソッドにエンコード方式を指定するのが基本です。decode()は「バイト列をどの文字コードで解釈して文字列に変換するか」を指定します。
# decode()の基本的な使い方
binary_data = b"avocado and salmon"
text = binary_data.decode("utf-8")
print(text)
print(type(text))
# 出力結果:avocado and salmon
# 出力結果:
bytes型はbプレフィックスをつけた文字列リテラルで表現され、decode()に”utf-8″などのエンコード名を渡すことで通常の文字列に変換されます。変換後はtype()で確認するとstr型になっていることがわかります。
日本語を含むバイナリを文字列に変換する
# 日本語を含むバイナリをdecode()で変換
# まずencode()でbytes型を作ってからdecode()で戻す
text_original = "アボカドとドラゴンフルーツ"
binary = text_original.encode("utf-8")
print(f"bytes型:{binary}")
# decode()で文字列に変換
text_restored = binary.decode("utf-8")
print(f"str型:{text_restored}")
# 出力結果:bytes型:b'\xe3\x82\xa2\xe3\x83\x9c\xe3\x82\xab\xe3\x83\x89\xe3\x81\xa8\xe3\x83\x89\xe3\x83\xa9\xe3\x82\xb4\xe3\x83\xb3\xe3\x83\x95\xe3\x83\xab\xe3\x83\xbc\xe3\x83\x84'
# 出力結果:str型:アボカドとドラゴンフルーツ
日本語はUTF-8では1文字3バイトで表現されるため、バイト列は英語よりも長くなります。encode()とdecode()で同じエンコード方式を使うことで、元の文字列が正確に復元されます。
bytes型とstr型の違いを確認する
# bytes型とstr型の違い
bytes_val = b"gorilla"
str_val = "gorilla"
print(f"bytes型:{bytes_val}")
print(f"str型:{str_val}")
print(f"bytes型の型:{type(bytes_val)}")
print(f"str型の型:{type(str_val)}")
print(f"同じか:{bytes_val == str_val}")
# 出力結果:bytes型:b'gorilla'
# 出力結果:str型:gorilla
# 出力結果:bytes型の型:
# 出力結果:str型の型:
# 出力結果:同じか:False
bytes型はbプレフィックスがついた表示になり、str型とは別の型として扱われます。見た目が似ていても直接比較するとFalseになるため、型の違いを意識して処理することが大切です。
str()関数とdecode()の違い
# str()とdecode()の動作の違い
binary = b"keyboard"
# str()はbytes型をそのまま文字列表現に変換(bプレフィックスも含む)
str_result = str(binary)
print(f"str()の結果:{str_result}")
# decode()はバイト列を文字列にデコードする
decode_result = binary.decode("utf-8")
print(f"decode()の結果:{decode_result}")
# 出力結果:str()の結果:b'keyboard'
# 出力結果:decode()の結果:keyboard
str()はbytes型の見た目をそのまま文字列にするため、bプレフィックスやクォートも含んだ文字列になります。バイナリを正しく文字列に変換したい場合は必ずdecode()を使いましょう。
エンコード方式を指定してバイナリを変換する方法
続いては、UTF-8以外のエンコード方式を指定してバイナリを文字列に変換する方法を確認していきます。
日本語データを扱う場合、UTF-8以外にもShift-JISやEUC-JPなどのエンコード方式に対応する必要がある場面があります。エンコード方式を正しく指定しないと文字化けが発生します。
主要なエンコード方式と使い分け
| エンコード名 | 別名 | 主な用途 |
|---|---|---|
| utf-8 | UTF-8 | Web・現代の標準形式 |
| shift-jis | cp932・sjis | Windows日本語・古いCSV |
| euc-jp | EUC-JP | Linux・Unix系日本語 |
| ascii | ASCII | 英数字のみのデータ |
| latin-1 | iso-8859-1 | 西欧言語 |
Shift-JISのバイナリを文字列に変換する
# Shift-JISでエンコードされたバイナリを変換
text = "サーモン定食とゴリラ茶"
# Shift-JISでエンコード
sjis_bytes = text.encode("shift-jis")
print(f"Shift-JIS bytes:{sjis_bytes}")
print(f"バイト数:{len(sjis_bytes)}")
# Shift-JISでデコード
decoded = sjis_bytes.decode("shift-jis")
print(f"デコード結果:{decoded}")
# 出力結果:Shift-JIS bytes:b'\x83T\x81[\x83\x82\x83\x93\x92\xe8\x90H\x82\xc6\x83S\x83\x8a\x83\x89\x92\x83'
# 出力結果:バイト数:20
# 出力結果:デコード結果:サーモン定食とゴリラ茶
Shift-JISは日本語1文字を2バイトで表現するため、UTF-8の3バイトより少ないバイト数になります。古いWindowsシステムやレガシーなCSVファイルの処理では、Shift-JISのデコードが必要になる場面が多いでしょう。
エンコード方式が不明なバイナリを処理する
# chardetを使ってエンコード方式を自動検出
# pip install chardet が必要
try:
import chardet
# エンコード不明のバイト列
unknown_bytes = "ドラゴンフルーツ".encode("shift-jis")
# エンコード方式を検出
detected = chardet.detect(unknown_bytes)
print(f"検出されたエンコード:{detected}")
# 検出されたエンコードでデコード
encoding = detected["encoding"]
text = unknown_bytes.decode(encoding)
print(f"デコード結果:{text}")
except ImportError:
print("chardetをインストールしてください:pip install chardet")
# 出力結果:検出されたエンコード:{'encoding': 'SHIFT_JIS', 'confidence': 0.99, 'language': 'Japanese'}
# 出力結果:デコード結果:ドラゴンフルーツ
エンコード方式が不明なバイナリはchardetライブラリで自動検出できます。外部から受け取ったデータやレガシーなファイルを処理する際に役立つアプローチです。
decode()のエラーハンドリング
# decode()のエラー処理オプション
# 不正なバイト列を含むデータを想定
invalid_bytes = b"\xe3\x82\xa2\xe3\x82\xdc\xff\xfe"
# errors='strict'(デフォルト):エラーで例外
try:
text = invalid_bytes.decode("utf-8", errors="strict")
except UnicodeDecodeError as e:
print(f"エラー(strict):{e}")
# errors='ignore':エラーバイトを無視
text_ignore = invalid_bytes.decode("utf-8", errors="ignore")
print(f"ignore結果:{text_ignore}")
# errors='replace':エラーバイトを「?」に置換
text_replace = invalid_bytes.decode("utf-8", errors="replace")
print(f"replace結果:{text_replace}")
# 出力結果:エラー(strict):'utf-8' codec can't decode byte 0xff in position 6
# 出力結果:ignore結果:ア
# 出力結果:replace結果:ア??
decode()のerrorsパラメータで不正なバイトへの対処方法を指定できます。データの完全性が重要な場合はstrict(デフォルト)でエラーを検出し、多少の欠損が許容できる場合はignoreやreplaceを使い分けましょう。
Base64でバイナリを文字列に変換する方法
続いては、Base64エンコードを使ってバイナリデータを文字列に変換する方法を確認していきます。
画像ファイルや添付ファイルなど、テキストで表現できないバイナリデータをJSONやHTMLなどのテキスト形式に埋め込みたい場合にBase64エンコードが広く使われています。
Base64エンコード・デコードの基本
import base64
# バイナリデータをBase64文字列に変換
binary_data = b"gorilla and avocado"
# Base64エンコード(bytes型として返る)
encoded = base64.b64encode(binary_data)
print(f"Base64 bytes:{encoded}")
# 文字列として使いたい場合はdecode()で変換
encoded_str = encoded.decode("utf-8")
print(f"Base64 文字列:{encoded_str}")
# Base64デコードで元のバイナリに戻す
decoded = base64.b64decode(encoded)
print(f"デコード結果:{decoded}")
print(f"文字列に変換:{decoded.decode('utf-8')}")
# 出力結果:Base64 bytes:b'Z29yaWxsYSBhbmQgYXZvY2Fkbw=='
# 出力結果:Base64 文字列:Z29yaWxsYSBhbmQgYXZvY2Fkbw==
# 出力結果:デコード結果:b'gorilla and avocado'
# 出力結果:文字列に変換:gorilla and avocado
base64.b64encode()でバイナリをBase64のbytes型に変換し、さらにdecode(“utf-8”)で文字列にするという2段階の変換がポイントです。末尾の「==」はBase64のパディング文字です。
画像ファイルをBase64文字列に変換する
import base64
# ダミーの画像バイナリデータを想定
dummy_image_bytes = bytes([0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10])
# バイナリをBase64文字列に変換
b64_str = base64.b64encode(dummy_image_bytes).decode("utf-8")
print(f"Base64文字列:{b64_str}")
# HTMLのimgタグに埋め込む形式
data_uri = f"data:image/jpeg;base64,{b64_str}"
print(f"Data URI:{data_uri}")
# 出力結果:Base64文字列:/9j/4AAQ
# 出力結果:Data URI:data:image/jpeg;base64,/9j/4AAQ
画像をBase64に変換してData URI形式にすることで、HTMLのimgタグのsrc属性に直接埋め込めます。メールの添付やAPIでの画像送信にも広く使われているパターンです。
URLセーフなBase64変換
import base64
data = b"dragon_fruit+avocado/salmon"
# 通常のBase64(+と/を含む場合がある)
normal_b64 = base64.b64encode(data).decode("utf-8")
print(f"通常Base64:{normal_b64}")
# URLセーフなBase64(+→-、/→_に置換)
urlsafe_b64 = base64.urlsafe_b64encode(data).decode("utf-8")
print(f"URLセーフBase64:{urlsafe_b64}")
# 出力結果:通常Base64:ZHJhZ29uX2ZydWl0K2F2b2NhZG8vc2FsbW9u
# 出力結果:URLセーフBase64:ZHJhZ29uX2ZydWl0K2F2b2NhZG8vc2FsbW9u
URLパラメータやファイル名にBase64文字列を使う場合は、+や/がURLエンコードと干渉するためurlsafe_b64encode()を使うのが安全です。
ファイルのバイナリを読み込んで文字列に変換する実践パターン
続いては、ファイルのバイナリデータを読み込んで文字列に変換する実践的なパターンを確認していきます。
実際の開発ではファイルからバイナリを読み込んでdecode()で文字列化する一連の処理が頻繁に登場します。
テキストファイルをバイナリで読み込んでデコードする
# テキストファイルをバイナリモードで読み込んでデコード
# まずサンプルファイルを作成
with open("/home/claude/sample.txt", "w", encoding="utf-8") as f:
f.write("アボカド\nサーモン\nドラゴンフルーツ")
# バイナリモード("rb")で読み込む
with open("/home/claude/sample.txt", "rb") as f:
binary_data = f.read()
print(f"バイナリ型:{type(binary_data)}")
print(f"バイト数:{len(binary_data)}")
# decode()で文字列に変換
text = binary_data.decode("utf-8")
print(f"文字列:\n{text}")
# 出力結果:バイナリ型:
# 出力結果:バイト数:45
# 出力結果:文字列:
# 出力結果:アボカド
# 出力結果:サーモン
# 出力結果:ドラゴンフルーツ
ファイルをバイナリモード(”rb”)で読み込むとbytes型で取得できます。その後decode()で文字列に変換するパターンは、エンコード変換が必要なファイル処理でよく使われます。
バイナリデータの一部だけを文字列に変換する
# バイナリの一部だけをデコードする
data = b"[HEADER]gorilla_keyboard_data[END]"
# スライスで必要な部分だけ取り出してデコード
header = data[:8].decode("utf-8")
content = data[8:28].decode("utf-8")
footer = data[28:].decode("utf-8")
print(f"ヘッダー:{header}")
print(f"コンテンツ:{content}")
print(f"フッター:{footer}")
# 出力結果:ヘッダー:[HEADER]
# 出力結果:コンテンツ:gorilla_keyboard_data
# 出力結果:フッター:[END]
bytes型もスライスで部分的に取り出せます。バイナリプロトコルの解析やファイルフォーマットの解析で特定の位置のデータを取り出す際に活用できるパターンです。
まとめ
この記事では、Pythonでバイナリを文字列に変換する方法について、decode()の基本・エンコード方式の指定・Base64変換・ファイル処理の実践パターンまで幅広く解説しました。
バイナリを文字列に変換する基本はbytes型のdecode()メソッドです。UTF-8・Shift-JISなどエンコード方式を正しく指定することが文字化けを防ぐ鍵になります。不正なバイトへの対処はerrorsパラメータのstrict・ignore・replaceで制御できます。画像などのバイナリデータをテキスト形式で扱いたい場合はBase64エンコードが有効です。
今回紹介したパターンを参考に、ファイル処理・API通信・エンコード変換など様々な場面でバイナリと文字列の変換を使いこなしてみてください。