Documentation Python

Pythonのbytes型は、不変(イミュータブル)なバイト列を表すデータ型です。テキストのエンコーディング、バイナリファイル操作、ネットワーク通信など、様々な場面で使用されます。

strとbytesの比較

特性strbytes
表現するもの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'
文字列→bytesstr.encode()'hello'.encode('utf-8')
bytes→文字列bytes.decode()b'hello'.decode('utf-8')
16進数変換hex(), fromhex()b'AB'.hex()
検索find(), inb'world' in b
分割split()b'a,b'.split(b',')
結合join()b','.join([b'a', b'b'])

bytes型はバイナリデータを安全に扱うための基本的な型です。ファイル操作、ネットワーク通信、暗号化など、様々な場面で活用されます。

参考文献

円