出力されたCSVファイルでカンマズレが発生する事象の調査をしたときの小ネタな話
事象
CSVファイルでカンマ「,」を含むデータがカンマの位置で分割され、かつ、カンマが増殖していた
ex.)
山田,太郎
が、
,山田,,太郎,
に変換されていた
原因
原因自体はとても単純で以下のような誤設定だった
csv.writer(delimiter=',', quotechar=',', quoting=csv.QUOTE_MINIMAL)
本来は「"」が設定されるはずのquotecharが「,」になっていたため、delimiterである「,」を含むフィールドが「,」でクオートされてしまい、文字列の先頭・末尾に「,」が付加されていた。
しかし、ここでもう一つ謎なのが、文字列中に含まれた「,」が「,,」になっていたこと。
csvモジュールのドキュメントを読んで行くと、以下のような仕様が記述されていました。
Dialect.doublequote
以下引用
フィールド内に現れた quotechar のインスタンスで、クオートではないその文字自身でなければならない文字をどのようにクオートするかを制御します。 True の場合、この文字は二重化されます。 False の場合、 escapechar は quotechar の前に置かれます。デフォルトでは True です。
doublequoteのデフォルトはTrueなのでデフォルトの挙動ではquotecharはquotecharでエスケープされるということ。
ちなみに doublequoteをFalseに設定するとescapechar(デフォルトは「\」)でエスケープされる。
ダブルクオートでエスケープするという慣れ親しんだエスケープと異なる挙動で混乱しますが、これにより文字列中に含まれた「,」がquotecharである「,」でエスケープされ「,,」になっていたことがわかりました。
気になったのでさらにCSVの仕様について調べると、以下の記述を見つけました。
RFC4180
2. Definition of the CSV Formatより以下引用
7. If double-quotes are used to enclose fields, then a double-quote appearing inside a field must be escaped by preceding it with another double quote. For example:
"aaa","b""bb","ccc"
ダブルクオートで囲まれるフィールドの中にダブルクオートが含まれる場合、ダブルクオートはダブルクオートでエスケープされる、となっていました。
ダブルクオートでのエスケープはCSVの御作法だったようです。
もろもろ話をまとめると、
クオート文字が「,」に設定されていたため、「,」を含むフィールドが「,」で囲まれ、かつ、フィールド内の「,」がクオート文字の「,」でエスケープされ「,,」になった
ということで、quotecharを「"」にすることで、期待通りの挙動になりました。(quotecharのデフォルトは「"」なので、quotechar指定を省略してもOK)