PythonのCSVで空行が増える?newline指定と改行コードをバイトで確かめる

Python

この記事について この記事は、生成AIを活用した自動生成フローで作成しています。Python公式のcsvとioの仕様を参照し、改行変換とCSVのレコード区切りを区別して確認する実験を整理しました。

検証ステータス:📘 公式仕様確認済み・掲載コードの実機実行未実施

PythonのCSVで空行が増える?newline指定と改行コードをバイトで確かめる

まず結論:CSVを開くときはnewlineを空文字にする

PythonでCSVを作ったところ、別の環境で開くと行の間に空行がある。あるいはセルの中に改行を入れたはずなのに、読み戻すと内容が変わっている。こうした現象は、データが壊れたと決めつける前に、CSVライブラリとテキストファイルの改行変換が二重に働いていないかを確認したいところです。

Pythonの公式ドキュメントは、ファイルをcsv.readerやcsv.writerに渡す場合、open(..., newline='')を使用するよう指定しています。これは「CSVには改行を入れない」という意味ではありません。テキストI/Oの自動改行変換を抑え、CSV自身が区切りと引用符を解釈できるようにする指定です。OSが異なると問題が見えたり見えなかったりするため、画面上の行数だけで判断しないのがポイントです。 外部サービスも既存ファイルも使いません。架空の備品データを一時ディレクトリへ書き、CSVとして読み戻すほか、実際のバイト列も観察します。期待値と確認点を区別し、環境ごとの結果を読者自身で確かめられるようにします。

対象環境と実験の準備

項目条件
対象Python 3の標準ライブラリcsv、pathlib、tempfile
OSWindows、macOS、Linuxで確認可能な設計
追加インストール不要
入力架空の備品番号、担当名、メモ
ファイル操作一時フォルダに新規作成し終了時に削除
検証範囲公式仕様照合済み。掲載コードは未実行

ここでの「行」は二種類あります。CSVの一レコードは一件のデータを指します。一方、テキストエディタが数える物理行は改行文字によって区切られます。引用符で囲まれたセルの中に改行がある場合、物理行が増えてもCSVのレコード数は増えません。最初にこの違いを意識すると、空行に見える現象を整理しやすくなります。

安全な実験:書く、読む、バイトを表示する

以下をcsv_newline_demo.pyとして保存し、Python 3で実行してください。書込み先は実行中だけ存在する一時ディレクトリです。既存の業務CSVを上書きしません。日本語の表示にはUTF-8を明示しています。

import csv
import tempfile
from pathlib import Path

rows = [
    ["番号", "担当", "メモ"],
    ["A01", "佐藤", "貸出中"],
    ["A02", "鈴木", "返却予定\n確認待ち"],
]

with tempfile.TemporaryDirectory() as work:
    path = Path(work) / "sample.csv"

    with path.open("w", encoding="utf-8", newline="") as f:
        writer = csv.writer(f)
        writer.writerows(rows)

    raw = path.read_bytes()
    print("CRLF個数:", raw.count(b"\r\n"))
    print("LF個数:", raw.count(b"\n"))
    print("先頭バイト:", repr(raw[:100]))

    with path.open("r", encoding="utf-8", newline="") as f:
        restored = list(csv.reader(f))

    print("CSVレコード数:", len(restored))
    print("一致:", restored == rows)
    print("セル内改行:", repr(restored[2][2]))

実験で重要なのは、単にエラーなく書けたことではありません。読み戻したrestoredが元のrowsと等しいかを調べています。二つのリストが一致するなら、少なくとも今回の入力については行数とセル内容が往復で保たれたと判断できます。反対に、ファイルが存在するだけでは、引用符や改行が正しく保存された証拠にはなりません。

raw.count(b"\r\n")はCRLFという二バイトの組を数え、raw.count(b"\n")はLFバイトを数えます。CRLFにはLFが含まれるので、この二つを足して「総行数」としてはいけません。セル内改行もLFとして数えられるため、バイト上の改行個数とCSVレコード数が異なるのは正常な場合があります。

観察ポイント:見かけの行数とデータ件数は別

期待されるCSVレコード数は見出しを含めて3件、一致はTrueです。最後のメモには、文字列として改行が一つ残ることを期待します。ただしこれは公式仕様に基づく期待結果であり、今回の制作環境で掲載コードを実行した実測値ではありません。

csv.writerの既定のexcel方言は、通常レコード末尾にCRLFを使います。一方、サンプルのセル内改行はPython文字列のLFです。したがってファイルをテキストエディタで見ると、メモの途中でも物理行が分かれます。これを「余計なCSVレコードが増えた」と解釈すると、正しいファイルを誤って修正するおそれがあります。

表計算ソフトの表示はさらに別の問題です。セル内改行を一つのセルとして表示するか、文字コードをどう推定するかは、そのソフトの取込み設定にも依存します。Python側でrestored == rowsが成立することと、すべての表計算ソフトで同じ見た目になることは同義ではありません。まずPythonで往復を確認し、次に利用するソフトで取込みテストをしてください。

仕組み:改行を扱う担当が二つある

次の図は、CSVライブラリが引用符やレコード区切りを担当し、テキストI/Oが文字とバイトの変換を担当するという役割分担を示します。どちらかが不要という意味ではなく、改行の自動変換を重ねないことが狙いです。

flowchart TD
    A["Pythonの行データ"] --> B["csv.writer: 引用符と区切りを構成"]
    B --> C["テキストI/O: UTF-8へ変換"]
    C --> D["CSVファイルのバイト列"]
    D --> E["テキストI/O: UTF-8を復号"]
    E --> F["csv.reader: レコードとセルを復元"]

newline=''は、テキストI/O側に改行の追加変換をさせないための指定です。特にWindowsのようにテキスト出力でLFをCRLFへ変換し得る環境では、CSVライブラリが既に作ったCRLFのLF部分を再変換すると、意図しないCRCRLFになる可能性があります。Python公式のcsvドキュメントにも、newline=''を省いた場合の問題が説明されています。

読み込み側でも注意が必要です。引用符で囲まれたセルに含まれる改行を、CSVライブラリが正しく判定できるよう、ファイルを開くときの改行処理を任せる必要があります。encoding='utf-8'は文字コードの指定、newline=''は改行変換の指定であり、役割が異なります。UTF-8を指定しただけで改行の問題が解決するわけではありません。

条件を一つ変える:newlineを省略したら

比較するときは書込み側のnewline=''だけを取り除き、残りのコードは同じにします。変更後も一時フォルダで実行し、元の出力とバイト数を比べてください。WindowsとLinuxでは結果が同じとは限りません。Linuxで違いが見えなかったとしても、指定が無意味と結論付けるのは早計です。

Windowsでテキスト出力の改行変換が働く場合、CSVが作ったCRLFが追加変換され、バイト列にCRCRLFが現れることがあります。そこでraw.count(b"\r\r\n")も表示すると、見た目の空行がどこから生まれたかを確認しやすくなります。環境依存の挙動を、すべてのOSで必ず起こる不具合として説明しないことが重要です。

読み込み側もnewline=''を維持したまま比較してください。書込みと読込みの条件を同時に変更すると、どちらが原因だったのか切り分けられません。変更を一つに限定するのは、Pythonの学習だけでなく障害調査全般で役立つ方法です。

失敗したときの切り分け

もし一致がFalseなら、まず件数だけでなく、どのセルが異なるかを調べます。引用符内の改行が変わったのか、文字コードが違うのか、列がずれたのかで対処は異なります。データを一括置換して改行を消す前に、reprで実際の制御文字を見てください。画面上では同じ空白に見えても、LFとCRLFは異なるバイト列です。

UnicodeDecodeErrorが発生した場合は、CSVの区切りよりも先に文字コードを疑います。外部から受け取ったCSVがUTF-8とは限りません。読み込み側の文字コードを送り手の仕様と照合し、推測で上書き保存しないでください。また、csv.readerは基本的に文字列として列を返すため、数字や日付が自動的に業務上の型へ変換されると期待しないことも大切です。

列数がずれる場合は、カンマを含むセルが適切に引用されているか、区切り文字が実際にカンマなのかを確認します。タブ区切りやセミコロン区切りのファイルをカンマCSVとして扱えば、改行設定が正しくても期待する列にはなりません。ファイル形式、文字コード、改行という三つの軸を別々に調べると、調査が混乱しません。

業務ではどう使い分けるか

社内システムから出力されたCSVを別のシステムへ渡す場合は、まず受け手の仕様を確認します。必要な区切り、引用符、文字コード、改行コード、ヘッダー有無が決まっているなら、それに合わせてcsv.writerの引数を指定します。単に「Excelで開けたから正しい」と判定せず、受け手が期待するレコード数と重要なセル内容を読み戻して検査することを勧めます。

セル内改行が許されないシステムなら、CSVライブラリの設定で無理に隠すのではなく、入力データをどう正規化するか業務ルールとして決めてください。改行をスペースに変えるのか、エスケープ表記へ変えるのか、入力を拒否するのかで意味が変わります。特に住所や備考欄の改行を勝手に削ると、読みやすさや証跡に影響する可能性があります。

既存の業務ファイルを扱うときは、元データを保持し、別名の検証ファイルで往復試験を行います。今回の例は一時フォルダなので後始末が自動ですが、本番の出力先へそのまま書き換える運用にはしていません。大量データでは全件をメモリへ読み込まず、行単位で件数と妥当性を検査する設計も必要です。

最後に覚えておきたいのは、newline=''は空行を削除する機能ではないということです。CSVの構文を担当する処理と、テキストの改行変換を担当する処理の衝突を避ける指定です。バイト列とCSVレコードの両方を見ると、見た目の問題とデータの破損を区別できます。

公式情報・一次情報

文書情報

記事タイトル
PythonのCSVで空行が増える?newline指定と改行コードをバイトで確かめる
作成日
更新日
Source URL
https://papanda925.com/?p=18569

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

タイトルとURLをコピーしました