LaTeX957300 views
雑学1472593 views
高校日本史189857 views
ヒストリア284143 views
中学英語808712 views
Computer365120 views
小学算数1194618 views
中学理科1626207 views
教育148875 views
中学社会667106 views
Help
Tools

English

バイナリモードとテキストモードの内部動作の違い

Python でファイルを開くとき、"r""rb" で何が違うのか。単に「改行コードの扱い」だけではない、内部動作の根本的な違いを解説する。

モードによる違いの概要

テキストモード("r", "w")

バイト列を文字列にデコード/エンコードする。改行コードの変換も行う

バイナリモード("rb", "wb")

バイト列をそのまま扱う。変換は一切行わない

テキストモードの内部処理

テキストモードでファイルを読むと、以下の処理が自動的に行われる。

ディスクからバイト列を読み込む

エンコーディングに従って文字列にデコード

改行コードを統一(\r\n → \n)

Python の str オブジェクトとして返す

# テキストモード
with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()  # str 型
    print(type(content))  # <class 'str'>

バイナリモードの内部処理

バイナリモードでは変換が行われない。

ディスクからバイト列を読み込む

そのまま bytes オブジェクトとして返す

# バイナリモード
with open("data.txt", "rb") as f:
    content = f.read()  # bytes 型
    print(type(content))  # <class 'bytes'>

改行コードの変換

テキストモードでは OS に応じた改行コードの変換が行われる。

読み込み時

\r\n(Windows)や \r(古い Mac)を \n に統一する

書き込み時

\n を OS のネイティブ改行コードに変換する(Windows では \r\n)

# Windows で作成されたファイル(\r\n 改行)
# テキストモード
with open("windows.txt", "r") as f:
    content = f.read()
    print(repr(content))  # 'line1\nline2\n'(\r が消える)

# バイナリモード
with open("windows.txt", "rb") as f:
    content = f.read()
    print(repr(content))  # b'line1\r\nline2\r\n'(そのまま)

この変換を無効にするには newline="" を指定する。

# 改行変換を無効にする
with open("data.txt", "r", newline="") as f:
    content = f.read()  # \r\n がそのまま残る

エンコーディングの自動検出

テキストモードではエンコーディングを指定しないと、システムのデフォルトエンコーディングが使われる。

import locale

# デフォルトエンコーディングを確認
print(locale.getpreferredencoding())  # UTF-8 など

# 明示的に指定するのがベストプラクティス
with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()
Linux / macOS

デフォルトは通常 UTF-8

Windows

デフォルトは CP932(Shift_JIS)などロケール依存。Python 3.15 から UTF-8 がデフォルトになる予定

バッファリングの違い

テキストモードとバイナリモードでバッファリングの動作も異なる。

# バイナリモードは buffering=0 が可能(バッファなし)
with open("data.bin", "rb", buffering=0) as f:
    pass  # OK

# テキストモードは buffering=0 不可
try:
    with open("data.txt", "r", buffering=0) as f:
        pass
except ValueError as e:
    print(e)  # can't have unbuffered text I/O

テキストモードでバッファなしにできないのは、エンコーディング変換がバッファを必要とするためだ。

io モジュールの構造

Python 3 の io モジュールは、階層的な構造を持っている。

FileIO

OS レベルの低レベル I/O。バイト列をそのまま扱う。

BufferedReader / BufferedWriter

FileIO をラップしてバッファリングを提供。

TextIOWrapper

BufferedReader/Writer をラップしてエンコーディング変換と改行処理を提供。

import io

# バイナリモードの内部構造
with open("data.txt", "rb") as f:
    print(type(f))  # <class '_io.BufferedReader'>
    print(type(f.raw))  # <class '_io.FileIO'>

# テキストモードの内部構造
with open("data.txt", "r") as f:
    print(type(f))  # <class '_io.TextIOWrapper'>
    print(type(f.buffer))  # <class '_io.BufferedReader'>
    print(type(f.buffer.raw))  # <class '_io.FileIO'>

seek() と tell() の違い

テキストモードでは seek()tell() の動作が複雑になる。

# バイナリモードはバイト位置で動作
with open("data.txt", "rb") as f:
    f.seek(10)  # 10 バイト目に移動
    pos = f.tell()  # 10

# テキストモードはエンコーディング依存
with open("data.txt", "r", encoding="utf-8") as f:
    # UTF-8 では 1 文字 = 1〜4 バイト
    # seek(10) が 10 文字目とは限らない
    f.seek(0)  # 先頭への seek は常に安全
    pos = f.tell()  # 不透明な値(実装依存)

テキストモードでは seek(0) 以外の seek() は避けるべきだ。任意の位置に移動したいならバイナリモードを使う。

使い分けの指針

用途モード理由
テキストファイルテキスト文字列として扱える、改行が統一される
画像・音声バイナリ変換されると壊れる
JSON・XMLテキスト文字列データとして解析する
ZIP・PDFバイナリバイナリフォーマット
ログ出力テキスト人間が読む文字列
ネットワークデータバイナリプロトコルに従う必要がある

まとめ

テキストモードの特徴

エンコーディング変換、改行変換、str 型で扱う。人間が読むファイル向け。

バイナリモードの特徴

変換なし、bytes 型で扱う。機械的なデータや正確なバイト操作が必要な場合向け。

迷ったら

エンコーディングを明示してテキストモードを使う。バイナリデータならバイナリモード。