各行をタプルにし、Counterで出現件数を比較します。並び順は無視し、同じ行が2件から1件へ減った場合も差分として残します。ID単位の更新判定とは別の処理です。
この記事の内容
対象・検証範囲:CPython 3.12.14 / Linux。掲載サンプルと期待する出力を照合。標準ライブラリのみ。
この比較で分かること
商品リストの並び順だけが変わったとき、テキストのdiffでは大量の変更に見える場合があります。ここではCSVを値として読み、行順を無視して「増えたレコード」「減ったレコード」を求めます。
同じ行が重複している可能性があるため、集合のsetではなくCounterを使います。たとえば同じ行が2件から1件へ減った場合、集合の要素は変わらなくても、件数には差があります。
重複を保つ比較コード
文字コードは読み込み前に合わせてください。次の関数は列名・列順が同じ入力を対象とし、値の前後の空白や大文字・小文字も区別します。
import csv
import io
from collections import Counter
def parse(text):
reader = csv.reader(io.StringIO(text, newline=""), strict=True)
header = next(reader, None)
if not header or len(set(header)) != len(header):
raise ValueError("有効なヘッダーが必要です")
if any(not name.strip() for name in header):
raise ValueError("列名が空です")
records = []
for row in reader:
if not row:
continue
if len(row) != len(header):
raise ValueError("列数が一致しません")
records.append(tuple(row))
return header, Counter(records)
def diff_csv(before, after):
before_header, old = parse(before)
after_header, new = parse(after)
if before_header != after_header:
raise ValueError("ヘッダーが一致しません")
return new - old, old - new
before = "id,name\n001,ノート\n001,ノート\n002,ペン\n"
after = "id,name\n002,ペン\n001,ノート\n003,ファイル\n"
added, removed = diff_csv(before, after)
for row, count in sorted(added.items()):
print("追加", row[0], row[1], count)
for row, count in sorted(removed.items()):
print("削除", row[0], row[1], count)
assert diff_csv("id\n001\n002\n", "id\n002\n001\n") == (Counter(), Counter())
期待する結果
追加 003 ファイル 1
削除 001 ノート 1
002の並びが変わったことは差分になりません。001,ノートは1件だけ減っているので、削除件数として1を返します。これは「001のデータがすべてなくなった」という意味ではありません。
実ファイルを比較するには、両方をutf-8-sigなど正しい文字コードで読み、文字列をdiff_csv()に渡します。返されたCounterの値が件数、キーが各列の値を並べたタプルです。出力もCSVにする場合は、文字列をカンマでつなげず、writerを使ってください。
更新された行を知りたい場合
旧データが001,ノート、新データが001,ノート改訂版なら、この比較では旧行の削除と新行の追加になります。同じIDの値が変わったという「更新」には分類しません。
更新を分類するには、IDが一意で空でないことを確かめたうえで、IDをキーに残りの列を比較する別の設計が必要です。重複IDを辞書にそのまま入れると後の行で上書きされるため、検出して止める処理を入れます。
差分が多すぎるときの確認
- 列名または列順が違う場合はエラーになります。
1と1.0、001と1は別の文字列です。- 前後の空白や、セル内のLFとCRLFの違いも値の違いになります。
- 両方のCSVが同じように壊れていると、差分なしでも正しいとは限りません。
この実装は小〜中規模のデータをメモリー上で比較するものです。差分が少ないことと、データの内容が正しいことは別に確認します。列数とキーの検査も併用してください。
参考にした公式資料
- Python公式: collections — Counter確認日:2026.09.20
- Python公式: csv — CSVファイルの読み書き確認日:2026.09.20
掲載サンプルは記載のPython環境で実行確認しています。すべてのOS・入力データを保証するものではありません。 編集・検証方針