web-dev-qa-db-ja.com

Python 2.6のcsvファイルに対する一般的なUnicode / UTF-8サポート

Pythonのcsvモジュールは、UTF-8/Unicodeが含まれていると正しく機能しません。 Pythonのドキュメント や他のWebページで、特定のケースで機能しますが、処理するエンコーディングを十分に理解し、適切なスニペットを使用する必要があります。

Python 2.6で動作する.csvファイルから文字列とUnicode文字列の両方を読み書きするにはどうすればよいですか?または、これはPython 2.6の制限ですか?単純な解決策はありませんか?

43
djen

http://docs.python.org/library/csv.html#examples で提供されているUnicodeを読み取る方法のコード例は、Python 2.6および2.7。

以下はUnicodeDictReaderです。これはutf-8で動作し、他のエンコーディングでも使用できますが、私はutf-8入力でのみテストしました。

つまり、csv行がcsv.readerによってフィールドに分割された後にのみUnicodeをデコードするという考え方です。

class UnicodeCsvReader(object):
    def __init__(self, f, encoding="utf-8", **kwargs):
        self.csv_reader = csv.reader(f, **kwargs)
        self.encoding = encoding

    def __iter__(self):
        return self

    def next(self):
        # read and split the csv row into fields
        row = self.csv_reader.next() 
        # now decode
        return [unicode(cell, self.encoding) for cell in row]

    @property
    def line_num(self):
        return self.csv_reader.line_num

class UnicodeDictReader(csv.DictReader):
    def __init__(self, f, encoding="utf-8", fieldnames=None, **kwds):
        csv.DictReader.__init__(self, f, fieldnames=fieldnames, **kwds)
        self.reader = UnicodeCsvReader(f, encoding=encoding, **kwds)

使用法(ソースファイルエンコーディングはutf-8):

csv_lines = (
    "абв,123",
    "где,456",
)

for row in UnicodeCsvReader(csv_lines):
    for col in row:
        print(type(col), col)

出力:

$ python test.py
<type 'unicode'> абв
<type 'unicode'> 123
<type 'unicode'> где
<type 'unicode'> 456
52

少し遅い答えですが、私は nicodecsv を使用して大きな成功を収めました。

32
Serafeim

提供されているモジュール here は、utf-8 csvで作業できるcsvモジュールのクールでシンプルなドロップイン置換のように見えます。

import ucsv as csv
with open('some.csv', 'rb') as f:
    reader = csv.reader(f)
    for row in reader:
        print row
22
itsadok

doc にはすでにUnicodeの例が使用されていますが、なぜ別の例を見つけるか、ホイールを再発明する必要があるのですか?

import csv

def unicode_csv_reader(unicode_csv_data, dialect=csv.Excel, **kwargs):
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    for line in unicode_csv_data:
        yield line.encode('utf-8')
7
YOU

確かに unicodecsvcsvモジュールの優れた代替品です。ソースコードでcsvunicodecsvに置き換えたところですが、これは魅力のように機能します。

4
GMLudo

pythonドキュメント で言及されているラッパー_unicode_csv_reader_は、Unicode文字列を受け入れます。これは、csvがUnicode文字列を受け入れないためです。 cvsは、エンコーディングやロケールを認識しておらず、取得した文字列をバイトとして扱います。つまり、ラッパーはUnicode文字列をエンコードし、バイトの文字列を作成します。次に、ラッパーがcsvからの結果を返すと、バイトを再度デコードします。つまり、UTF-8バイトシーケンスを正しいUnicode文字に変換します。

ラッパーにプレーンバイト文字列を指定すると、 f.readlines()を使用すると、バイト数が127より大きいUnicodeDecodeErrorが得られます。プログラムにCSV形式のUnicode文字列がある場合は、ラッパーを使用します。

ラッパーにはまだ1つの制限があると想像できます。cvsはUnicodeを受け入れず、マルチバイト区切り文字も受け入れないため、区切り文字としてUnicode文字を含むファイルを解析できません。

3
gaston

tablib を検討する必要があります。これは完全に異なるアプローチですが、「動作するだけ」という要件の下で検討する必要があります。

with open('some.csv', 'rb') as f:
    csv = f.read().decode("utf-8")

import tablib
ds = tablib.Dataset()
ds.csv = csv
for row in ds.dict:
    print row["First name"]

警告:tablibは、すべての行に同じ数の項目がない場合、csvを拒否します。

2
itsadok

多分これは明らかに明白ですが、初心者のために私はそれを言及します。

python 3.Xでcsvモジュール は、そのままのエンコーディングをサポートしています 。このバージョンを使用すると、次のことができます。標準モジュールにこだわる。

 with open("foo.csv", encoding="utf-8") as f: 
     r = csv.reader(f, delimiter=";")
     for row in r: 
     print(row)

詳細については、次を参照してください: python 3.1.3はcsvモジュールでUnicodeをサポートしていますか?

2
jb.

Maxim's answer のわずかに改善されたバージョンは、UTF-8 BOMをスキップすることもできます。

import csv
import codecs

class UnicodeCsvReader(object):
    def __init__(self, csv_file, encoding='utf-8', **kwargs):
        if encoding == 'utf-8-sig':
            # convert from utf-8-sig (= UTF8 with BOM) to plain utf-8 (without BOM):
            self.csv_file = codecs.EncodedFile(csv_file, 'utf-8', 'utf-8-sig')
            encoding = 'utf-8'
        else:
            self.csv_file = csv_file
        self.csv_reader = csv.reader(self.csv_file, **kwargs)
        self.encoding = encoding

    def __iter__(self):
        return self

    def next(self):
        # read and split the csv row into fields
        row = self.csv_reader.next() 
        # now decode
        return [unicode(cell, self.encoding) for cell in row]

    @property
    def line_num(self):
        return self.csv_reader.line_num

class UnicodeDictReader(csv.DictReader):
    def __init__(self, csv_file, encoding='utf-8', fieldnames=None, **kwds):
        reader = UnicodeCsvReader(csv_file, encoding=encoding, **kwds)
        csv.DictReader.__init__(self, reader.csv_file, fieldnames=fieldnames, **kwds)
        self.reader = reader

BOMの存在は自動的に検出されないことに注意してください。 UnicodeCsvReaderまたはUnicodeDictReaderのコンストラクターにencoding='utf-8-sig'引数を渡して、そこにあることを通知する必要があります。エンコードutf-8-sigは、BOMを使用したutf-8です。

1
Matthias

Itsadokの答えを追加します。デフォルトでは、Excelはcsvファイルをlatin-1(ucsvはサポートしていません)として保存します。これは次の方法で簡単に修正できます。

with codecs.open(csv_path, 'rb', 'latin-1') as f:
    f = StringIO.StringIO( f.read().encode('utf-8') )

reader = ucsv.UnicodeReader(f)
# etc.
0
user2426679