先に、答え。

カンマや改行がセル内にあるCSVは、splitではなくcsv.readerとcsv.writerで扱います。ファイルはnewline=""で開き、書き出したCSVを読み直して値の一致を確認します。

この記事の内容

対象・検証範囲:CPython 3.12.14 / Linux。掲載サンプルと期待する出力を照合。標準ライブラリのみ。

文字列の分割では列がずれる理由

商品名が「ノート, A5」の場合、そのカンマは列の区切りではなく値の一部です。備考に改行が入ると、画面上の1行とCSVの1レコードも一致しません。

この記事の対象は、カンマ区切り・二重引用符で囲む一般的なCSVです。独自のエスケープ仕様を使うシステムでは、区切り文字や引用符の設定を合わせる必要があります。

カンマ・引用符・改行を含む例

次の入力は商品データが2件です。\nは値の中の改行を表しています。サンプルはCSV文字列を手で組み立てず、まずwriterで正しいCSVを作ります。

import csv
import io

original = [
    ["id", "name", "note"],
    ["001", "ノート, A5", '色は"青"'],
    ["002", "ペン", "1行目\n2行目"],
]
buffer = io.StringIO(newline="")
writer = csv.writer(buffer, lineterminator="\n")
writer.writerows(original)
csv_text = buffer.getvalue()
print(csv_text, end="")

restored = list(csv.reader(io.StringIO(csv_text, newline="")))
assert restored == original
print("読み直した値がすべて一致")
print("データ件数:", len(restored) - 1)

期待する結果

id,name,note
001,"ノート, A5","色は""青"""
002,ペン,"1行目
2行目"
読み直した値がすべて一致
データ件数: 2

二重引用符が""になるのは、値の中の引用符を表すためです。表示上は備考が次の行へ続いていますが、readerで読むと同じレコードの値に戻ります。

実ファイルでは、読み書き両方のopen()newline=""を指定し、文字コードも明示します。出力時のlineterminatorは相手の仕様に合わせます。この例は見やすさのためLFを指定しており、CRLFが必要な連携先では変更が必要です。

元のファイルとバイトまで同じになる?

このテストで一致させるのは解析後の値です。引用符を付ける場所や改行コードまで元CSVと同じになる保証はありません。値が同じでも、ファイル同士の文字列比較では差分が出ることがあります。

値の比較をしたい場合は、CSVとして読み込んだ後に比較します。行順を無視して差分を取る方法では、重複行も考慮します。

うまくいかない条件

  • 引用符が閉じていない入力は、意図した表に復元できないことがあります。
  • 区切り文字がタブなら、delimiter="\t"を設定します。
  • splitlines()で先に行分割すると、セル内改行の扱いを誤りやすくなります。
  • 列数の揃っていない入力は、読み込み成功だけでは正常と判断できません。

列の構造も調べたい場合は、CSVの列数とヘッダーを検査する例へ進んでください。CSVの読み込みは、構文と期待するデータの条件を分けて確認するのが確実です。

参考にした公式資料

掲載サンプルは記載のPython環境で実行確認しています。すべてのOS・入力データを保証するものではありません。 編集・検証方針