More than 1 year has passed since last update.

Pythonのcsvパッケージは大変便利です。面倒なエスケープ処理をちゃんと行ってくれます。とりわけ、Excelファイルで送られてきたファイルを処理するのに重宝します。なんといっても、dialect='excel'でExcel CSVをちゃんと読めます。

ところが、日本語が絡むと問題は途端に厄介になります。問題の本質は何か。まず、ExcelのTSVの扱いに関して、以下のことが知られています。

  • Excelのtsvエクスポートはutf-16(BOM付き)である
  • Excelが直接読めるのはBOM付きutf-16のtsvである(カンマ区切りはウィザードが必要)
  • csv(カンマ区切り)出力や読み込みも可能だがおすすめできない(後述)

このことからわかるのは、BOM付きutf-16ファイルを扱うのが最善ということです。これを扱う一番の方法は、ioパッケージを使うことです。

with io.open(path, encoding='utf-16') as f:
  ...

さらっと書きましたが、ioパッケージを使うのが実は肝です。utf-16はそこそこに扱いにくいフォーマットです。まず、BOMがいます。さらにこの状態でcr+lfされると、バイト単位では間に\0が挟まるということです。ですので、普通にopen()を使うと謎の挙動をします。

さてここで問題が起こります。python2系を使っていると、csvパッケージはunicodeをサポートしていません。従って、上の方法で開くと読めません。ここで、日和ってopenを使うと、上記の理由で読むのに苦労します。以前は私はshift-jisのcsvでエクスポートしていたんですが、Excelは内部的にUnicodeのようで、濁点が別文字になった正規化されてない状態のことがあり、このときshift-jis出力したら濁点が全部消失しました。なのでこの方法もおすすめしません。

実はUnicodeをcsvで読む方法は、マニュアルの最後に書いてあります。正解は、unicodeのストリームを一旦utf-8にエンコードして、この状態でcsvパッケージで読ませて、スプリットしたら再度utf-8をデコードすればよいのです。書き込む時も同じで、いったんunicodeをutf-8にしてStringIOに書き込んで、ファイルに吐き出す直前で再度unicodeに変換すればいいのです。圧倒的に2度手間ですが、長年Excelと格闘して最終的に一番安定した出力が得られませいた。ちなみに、Python3ではcsvパッケージがstr(2で言うところのunicode)をサポートしたので、もはや普通にioで開いて処理すればいいです。

流石にこれらの処理を毎回書いていると日が暮れるので、ライブラリにしました。

https://github.com/unnonouno/excelcsv

上記の問題を全て回避しています、たぶん。

やってはいけないこと

以上が全てですが、おさらい的にやってはいけないことと理由を羅列してみましょう。

要素ごとにutf-16でencode

.encode('utf-16')はBOM付きutf-16文字列を生成します。ここが、その他のエンコードとの決定的な違いです。

>>> u'a'.encode('utf-16')
'\xff\xfea\x00'

[x.encode('utf-16') for x in row]の様なコードを書くと、アチラコチラにBOMが入った謎の文字列が生成されしまいます。このアプローチはダメです。ファイルの先頭にだけBOMが入るようにしないといけません。
これを回避する一番よい方法は、原則的にunicodeで扱って、ファイル出力の部分で制御することです。これをやってくれるのがioパッケージであり、通常のopenを使って自前でエンコードするべきではありません。
別の回避策として、一旦StringIOにutf-8で出力しておいて、ファイルに書き込む前に全体をunicodeに変換してから、再度utf-16に変換し直すという方法もあります。が、ioを使ったほうがすっきりします。

openで開いてパースしてからデコード

utf-16ということは、パースしたい改行文字やタブ文字も2バイトで書かれているということです。1バイト単位で処理するPython2のcsvパッケージに処理させると、不要な\0文字が残ってしまいます。このアプローチが使えるのは、Ascii文字がそのまま残っているutf-8やshift-jisなどだけです。

shift-jisのcsvでエクスポートしてから読み込み

エクセルは内部的にはUnicodeでデータを持っているようです。最悪なのは、濁点が別文字として保存されていることがあり、この状態でshift-jisでエクスポートすると、濁点が全部消えます。正規化してくれればいいのに。そのため、excelからのエクスポートはutf-16テキスト一択になります。扱いづらいですがしかたありません。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.