modalsoul’s blog

これは“失敗”と呼べるかもしれないが、ぼくは“学習体験”と呼びたい

Pythonのcsvモジュールの挙動とRFC4180

出力されたCSVファイルでカンマズレが発生する事象の調査をしたときの小ネタな話

事象

CSVファイルでカンマ「,」を含むデータがカンマの位置で分割され、かつ、カンマが増殖していた

ex.)

山田,太郎

が、

,山田,,太郎,

に変換されていた

原因

原因自体はとても単純で以下のような誤設定だった

csv.writer(delimiter=',', quotechar=',', quoting=csv.QUOTE_MINIMAL)

本来は「"」が設定されるはずのquotecharが「,」になっていたため、delimiterである「,」を含むフィールドが「,」でクオートされてしまい、文字列の先頭・末尾に「,」が付加されていた。

しかし、ここでもう一つ謎なのが、文字列中に含まれた「,」が「,,」になっていたこと。

csvモジュールのドキュメントを読んで行くと、以下のような仕様が記述されていました。

Dialect.doublequote

以下引用

フィールド内に現れた quotechar のインスタンスで、クオートではないその文字自身でなければならない文字をどのようにクオートするかを制御します。 True の場合、この文字は二重化されます。 False の場合、 escapechar は quotechar の前に置かれます。デフォルトでは True です。

doublequoteのデフォルトはTrueなのでデフォルトの挙動ではquotecharquotecharエスケープされるということ。

ちなみに doublequoteFalseに設定すると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)