テクノロジー · 実用ガイド
Python CSVでNoneと空文字列が同じになる?実行版と交換規則を確認
引用符オプションが存在しても、読み取り動作まで実装されているとは限らない。20件の実測で、解析の問題、復元できない出力時の欠落、文字列かNoneだけを扱う代替規則を分ける。

CPython3.12.14でcsv.QUOTE_NOTNULLを使い[None, ""]を書き込むと、引用符なしの空フィールドと引用符付きの空フィールドができた。同じオプションで読むと、両方とも空文字列になった。定数があっても、稼働中のリーダーがこの区別を保つとは限らない。
最初に元のバイト列と、出力側が定めた欠損値の規則を確認する。以前の出力で欠損と意図した空文字列が同じ表現になっていれば、読み取りオプションだけで元の意味は復元できない。区別が必要ならファイルを保存し、元データから再出力する。
1. どこで情報が消えたかを特定する
元ファイルは変更しない。機密情報を含まない小さな例をrepr()で調べ、カンマ、引用符、レコード終端を見る。既定のcsv.writerによる往復では[None, ""]が["", ""]になり、出力時に情報が消えた。NOTNULLのライターは異なる表現を残したが、3.12のリーダーが同じ値にした。後者は解析側の互換性の境界だ。
読み取った空文字列をすべてNoneにする方法では修復できない。2フィールドの例では両方がNoneになり、意図した空文字列を失った。エスケープ規則のない代替記号も衝突する。Noneを<NULL>にすると、本物の文字列<NULL>と見分けられない。
2. 読み書きの両方と1列だけの行を試す
sys.versionを表示し、本番と同じオプションでNone、空文字列、"001"を含む例を読み書きする。Python3.12のリファレンスは、QUOTE_NOTNULLとQUOTE_STRINGSがリーダーに影響しない不具合を明記している。読み取りの変更は3.13で文書化されている。実際に実行したのは3.12.14だけだ。
Noneだけの行も試す。実測したQUOTE_NOTNULLライターはcsv.Error: single empty field record must be quotedを送出した。空文字列1個の行は[""]、空白レコードは[]として読めた。引用符で欠損を区別する規則では、エラー回避のためにnullを引用符で囲むと空の文字列を表すことになる。
3. 変換前に受信側と形式を合意する
対応する新しいリーダーでは、文書上のQUOTE_NOTNULLは引用符なしの空フィールドをNoneにし、それ以外を文字列にする。整数や任意のPythonオブジェクトは復元しない。QUOTE_STRINGSには数値変換の規則があり、汎用オブジェクト直列化ではない。採用前に両端の実環境で、対応する行の形をすべて検証する。
PostgreSQL COPY CSVの既定規則では、引用符なしの空フィールドがNULL、引用符付きが空文字列になる。FORCE_NULLとFORCE_NOT_NULLで解釈が変わる。これはPostgreSQLの文書上の規則であり、CSV共通の保証ではない。データベースやPython3.13との結合試験は実施していない。
4. 両端を管理できる場合の限定的な代替
旧環境でstrまたはNoneだけを交換するなら、実測したアプリケーション層ではNoneを文字N、すべての文字列をSと元の全文の連結で符号化する。空文字列はS、文字列NはSN、文字列<NULL>はS<NULL>になる。そのフィールドを通常のcsv.writerに渡し、カンマ、引用符、埋め込み改行はCSV層に処理させる。
復号時は完全一致するNだけをNoneにする。Sで始まるフィールドは先頭1文字だけを除去し、それ以外は不正なタグとして拒否する。書き込み前に文字列でもNoneでもない値を拒否する。これは新たに明示合意するアプリ形式だ。既存の普通のCSVに無条件で適用したり、COPY、pandas、表計算ソフトがタグを理解すると考えたりしない。
5. 値、型、行の形が一致して完了
20件のアサーションは、2か所の情報欠落、単一null行の書き込み例外、空白レコードと空フィールド、空値変換と代替記号の衝突、13値のタグ往復を検証した。None、空文字列、"001"、"0"、nullやタグに見える文字列、カンマ、引用符、埋め込み改行、Unicodeを含め、値と型の両方が一致した。
単一null、単一空文字列、null2個の行、整数と真偽値の拒否、不正タグと未符号化の空フィールドの拒否も検証した。確認できたのはCPython3.12.14/Linux上の文字列かNoneという規則だけだ。他の版や下流ソフトの認証ではない。各境界に代表例を残し、元の区別が消えていたら推測せず再出力する。
