Pythonのbytes型は、不変(イミュータブル)なバイト列を表すデータ型です。テキストのエンコーディング、バイナリファイル操作、ネットワーク通信など、様々な場面で使用されます。
strとbytesの比較
| 特性 | str | bytes |
|---|---|---|
| 表現するもの | Unicode文字列 | バイト列 |
| 要素の型 | 文字(str) | 整数(0-255) |
| リテラル | 'hello' | b'hello' |
| 変更可能性 | 不変 | 不変 |
| 用途 | テキスト処理 | バイナリ処理 |
bytesの作成
基本的な作成方法
# バイトリテラル
b1 = b'hello'
print(b1) # b'hello'
# 空のbytes
b2 = b''
print(b2) # b''
# ゼロ初期化(指定サイズ)
b3 = bytes(5)
print(b3) # b'\x00\x00\x00\x00\x00'
# 整数リストから作成
b4 = bytes([72, 101, 108, 108, 111]) # 'Hello'のASCIIコード
print(b4) # b'Hello'
# rangeから作成
b5 = bytes(range(65, 70)) # 'ABCDE'
print(b5) # b'ABCDE'
文字列からの変換(エンコード)
# 文字列をbytesに変換(encode)
text = "Hello, World!"
b = text.encode('utf-8')
print(b) # b'Hello, World!'
# 日本語のエンコード
japanese = "こんにちは"
b_utf8 = japanese.encode('utf-8')
b_sjis = japanese.encode('shift_jis')
b_eucjp = japanese.encode('euc-jp')
print(f"UTF-8: {len(b_utf8)} bytes") # 15 bytes
print(f"Shift_JIS: {len(b_sjis)} bytes") # 10 bytes
print(f"EUC-JP: {len(b_eucjp)} bytes") # 10 bytes
# bytes()コンストラクタでも可能
b = bytes("Hello", 'utf-8')
print(b) # b'Hello'
16進数からの作成
# 16進数文字列からbytesを作成
b = bytes.fromhex('48 65 6c 6c 6f')
print(b) # b'Hello'
# スペースなしでも可
b = bytes.fromhex('48656c6c6f')
print(b) # b'Hello'
# 大文字小文字は区別しない
b = bytes.fromhex('48 65 6C 6C 6F')
print(b) # b'Hello'
要素へのアクセス
b = b'Hello'
# インデックスアクセス(整数が返る)
print(b[0]) # 72 ('H'のASCIIコード)
print(b[-1]) # 111 ('o'のASCIIコード)
# スライス(bytesが返る)
print(b[0:2]) # b'He'
print(b[2:]) # b'llo'
print(b[::2]) # b'Hlo'
# 各バイトをイテレート
for byte in b:
print(byte, chr(byte))
# 72 H
# 101 e
# 108 l
# 108 l
# 111 o
# 長さの取得
print(len(b)) # 5
bytesの不変性
b = b'Hello'
# 要素の変更は不可
try:
b[0] = 72 # TypeError
except TypeError as e:
print(f"Error: {e}")
# Error: 'bytes' object does not support item assignment
# 新しいbytesを作成する必要がある
b_modified = b'J' + b[1:]
print(b_modified) # b'Jello'
bytesの操作
連結と繰り返し
b1 = b'Hello'
b2 = b' World'
# 連結
b3 = b1 + b2
print(b3) # b'Hello World'
# 繰り返し
b4 = b'ab' * 3
print(b4) # b'ababab'
検索
b = b'Hello World'
# in演算子
print(b'World' in b) # True
print(b'world' in b) # False(大文字小文字は区別)
# find: 最初の出現位置(見つからなければ-1)
print(b.find(b'o')) # 4
print(b.find(b'o', 5)) # 7(位置5以降で検索)
print(b.find(b'x')) # -1
# index: findと同様だが見つからなければ例外
print(b.index(b'o')) # 4
# rfind: 最後の出現位置
print(b.rfind(b'o')) # 7
# count: 出現回数
print(b.count(b'o')) # 2
print(b.count(b'l')) # 3
分割と結合
b = b'one,two,three'
# split: 分割
parts = b.split(b',')
print(parts) # [b'one', b'two', b'three']
# split with maxsplit
print(b.split(b',', 1)) # [b'one', b'two,three']
# join: 結合
joined = b'-'.join([b'a', b'b', b'c'])
print(joined) # b'a-b-c'
# splitlines: 改行で分割
text = b'line1\nline2\r\nline3'
print(text.splitlines()) # [b'line1', b'line2', b'line3']
変換
b = b'Hello World'
# 大文字・小文字
print(b.upper()) # b'HELLO WORLD'
print(b.lower()) # b'hello world'
print(b.swapcase()) # b'hELLO wORLD'
print(b.capitalize()) # b'Hello world'
print(b.title()) # b'Hello World'
# 置換
print(b.replace(b'World', b'Python')) # b'Hello Python'
print(b.replace(b'l', b'L', 1)) # b'HeLlo World'(1回だけ)
# strip(空白除去)
b2 = b' hello '
print(b2.strip()) # b'hello'
print(b2.lstrip()) # b'hello '
print(b2.rstrip()) # b' hello'
# 特定の文字を除去
b3 = b'xxhelloxx'
print(b3.strip(b'x')) # b'hello'
判定メソッド
# startswith / endswith
b = b'Hello World'
print(b.startswith(b'Hello')) # True
print(b.endswith(b'World')) # True
print(b.startswith((b'Hi', b'Hello'))) # True(タプルで複数指定可)
# その他の判定
print(b'123'.isdigit()) # True
print(b'abc'.isalpha()) # True
print(b'abc123'.isalnum()) # True
print(b'ABC'.isupper()) # True
print(b'abc'.islower()) # True
print(b' '.isspace()) # True
エンコーディングとデコーディング
基本的な変換
# str → bytes (エンコード)
text = "Hello, World!"
encoded = text.encode('utf-8')
print(encoded) # b'Hello, World!'
# bytes → str (デコード)
decoded = encoded.decode('utf-8')
print(decoded) # Hello, World!
# エンコーディングの種類
encodings = ['utf-8', 'ascii', 'latin-1', 'utf-16', 'utf-32']
text = "ABC"
for enc in encodings:
b = text.encode(enc)
print(f"{enc:10}: {b.hex()} ({len(b)} bytes)")
# utf-8 : 414243 (3 bytes)
# ascii : 414243 (3 bytes)
# latin-1 : 414243 (3 bytes)
# utf-16 : fffe410042004300 (8 bytes) ※BOM含む
# utf-32 : fffe0000410000004200000043000000 (16 bytes) ※BOM含む
エラーハンドリング
# エンコードできない文字がある場合
text = "Hello 🌍 World"
# strict(デフォルト): エラーを発生
try:
text.encode('ascii')
except UnicodeEncodeError as e:
print(f"Error: {e}")
# ignore: エンコードできない文字を無視
print(text.encode('ascii', errors='ignore'))
# b'Hello World'
# replace: ?で置換
print(text.encode('ascii', errors='replace'))
# b'Hello ? World'
# xmlcharrefreplace: XML文字参照で置換
print(text.encode('ascii', errors='xmlcharrefreplace'))
# b'Hello 🌍 World'
# backslashreplace: バックスラッシュエスケープで置換
print(text.encode('ascii', errors='backslashreplace'))
# b'Hello \\U0001f30d World'
# デコードのエラーハンドリング
invalid = b'\xff\xfe'
print(invalid.decode('utf-8', errors='ignore')) # ''
print(invalid.decode('utf-8', errors='replace')) # '��'
バイナリファイル操作
ファイルの読み書き
# バイナリ書き込み
data = b'\x00\x01\x02\x03\x04\x05'
with open('data.bin', 'wb') as f:
f.write(data)
# バイナリ読み込み
with open('data.bin', 'rb') as f:
content = f.read()
print(content) # b'\x00\x01\x02\x03\x04\x05'
# 部分読み込み
with open('data.bin', 'rb') as f:
chunk = f.read(3) # 3バイト読み込み
print(chunk) # b'\x00\x01\x02'
チャンク読み込み
def read_chunks(filepath: str, chunk_size: int = 4096):
"""大きなファイルをチャンクで読み込み"""
with open(filepath, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
# 使用例
# for chunk in read_chunks('large_file.bin'):
# process(chunk)
16進数表現
b = b'Hello'
# bytesを16進数文字列に
print(b.hex()) # 48656c6c6f
print(b.hex(' ')) # 48 65 6c 6c 6f(区切り文字付き)
print(b.hex(':')) # 48:65:6c:6c:6f
print(b.hex(':', 2)) # 4865:6c6c:6f(2バイトごと)
# 16進数文字列からbytes
b2 = bytes.fromhex('48656c6c6f')
print(b2) # b'Hello'
実践的な使用例
HTTPリクエストの構築
def build_http_request(host: str, path: str = '/') -> bytes:
"""シンプルなHTTP GETリクエストを構築"""
request = f"GET {path} HTTP/1.1\r\n"
request += f"Host: {host}\r\n"
request += "Connection: close\r\n"
request += "\r\n"
return request.encode('ascii')
# 使用例
request = build_http_request('example.com', '/api/data')
print(request)
# b'GET /api/data HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n'
Base64エンコーディング
import base64
# bytes → Base64
original = b'Hello, World!'
encoded = base64.b64encode(original)
print(encoded) # b'SGVsbG8sIFdvcmxkIQ=='
# Base64 → bytes
decoded = base64.b64decode(encoded)
print(decoded) # b'Hello, World!'
# URLセーフなBase64
url_safe = base64.urlsafe_b64encode(original)
print(url_safe) # b'SGVsbG8sIFdvcmxkIQ=='
ハッシュ計算
import hashlib
data = b'Hello, World!'
# MD5
md5_hash = hashlib.md5(data).hexdigest()
print(f"MD5: {md5_hash}")
# SHA-256
sha256_hash = hashlib.sha256(data).hexdigest()
print(f"SHA-256: {sha256_hash}")
# ファイルのハッシュ
def file_hash(filepath: str, algorithm: str = 'sha256') -> str:
"""ファイルのハッシュ値を計算"""
h = hashlib.new(algorithm)
with open(filepath, 'rb') as f:
while chunk := f.read(8192):
h.update(chunk)
return h.hexdigest()
構造化データのパック/アンパック
import struct
# パック(Pythonの値をbytesに変換)
# 'i' = int (4 bytes), 'f' = float (4 bytes), '10s' = 10-byte string
packed = struct.pack('if10s', 42, 3.14, b'Hello')
print(packed.hex(' '))
# アンパック(bytesをPythonの値に変換)
unpacked = struct.unpack('if10s', packed)
print(unpacked) # (42, 3.140000104904175, b'Hello\x00\x00\x00\x00\x00')
# フォーマット文字
# 'b' = signed char (1 byte)
# 'B' = unsigned char (1 byte)
# 'h' = short (2 bytes)
# 'H' = unsigned short (2 bytes)
# 'i' = int (4 bytes)
# 'I' = unsigned int (4 bytes)
# 'q' = long long (8 bytes)
# 'f' = float (4 bytes)
# 'd' = double (8 bytes)
# エンディアン指定
# '<' = リトルエンディアン
# '>' = ビッグエンディアン
# '!' = ネットワークバイトオーダー(ビッグエンディアン)
packed_le = struct.pack('<i', 0x12345678)
packed_be = struct.pack('>i', 0x12345678)
print(f"Little-endian: {packed_le.hex()}") # 78563412
print(f"Big-endian: {packed_be.hex()}") # 12345678
bytesとbytearrayの使い分け
# bytes: 読み取り専用のデータに使用
config = b'{"key": "value"}' # 設定データ
response = b'HTTP/1.1 200 OK' # レスポンスヘッダー
# bytearray: 変更が必要なデータに使用
buffer = bytearray(1024) # バッファ
for i in range(len(buffer)):
buffer[i] = i % 256
# bytesはハッシュ可能(辞書キーに使える)
cache = {}
cache[b'key1'] = 'value1'
print(cache[b'key1']) # value1
# bytearrayはハッシュ不可
# cache[bytearray(b'key2')] = 'value2' # TypeError
まとめ
| 操作 | メソッド/構文 | 例 |
|---|---|---|
| 作成 | b'...', bytes() | b'hello' |
| 文字列→bytes | str.encode() | 'hello'.encode('utf-8') |
| bytes→文字列 | bytes.decode() | b'hello'.decode('utf-8') |
| 16進数変換 | hex(), fromhex() | b'AB'.hex() |
| 検索 | find(), in | b'world' in b |
| 分割 | split() | b'a,b'.split(b',') |
| 結合 | join() | b','.join([b'a', b'b']) |
bytes型はバイナリデータを安全に扱うための基本的な型です。ファイル操作、ネットワーク通信、暗号化など、様々な場面で活用されます。