3

Python で CSV にデータを書き込もうとすると、次のエラーが表示されます。

File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/csv.py", line 150, in writerows
UnicodeEncodeError: 'ascii' codec can't encode character u'\xd3' in position 0: ordinal not in range(128)

CSVに書き込もうとしている辞書の例を次に示します。

{'Field1': 'Blah \xc3\x93 D\xc3\xa1blah', 'Field2': u'\xd3', 'Field3': u'Blah', 'Field4': u'D\xe1blah'}

Python で Unicode を CSV に書き込めないことはわかっていますが、何を変換するか、どのように変換するかがわかりません。

編集:これは私が試したことです。 dictList別の CSV から取得した辞書のリストです。

WANTED_HEADERS = ['First Name',
                  'Last Name',
                  'Date',
                  'ID']

def utf8ify(d):
  return dict((str(k).encode('utf-8'), str(v).encode('utf-8')) for k, v in d.iteritems())

def ListToCSVWithHeaders(data_list, output_file_name, headers):
output_file = open(output_file_name, 'w')
header_row = {}
to_append = []
for entry in data_list:
  to_append.append(utf8ify(entry))
  for key in entry.keys():
    if key not in headers:
      headers.append(key)
      print 'KEY APPENDED: ' + key
for header in headers:
  header_row[header] = header
data = [header_row]
data.extend(to_append)
data_writer = csv.DictWriter(output_file, headers)
data_writer.writerows(data)
print str(len(data)) + ' rows written'

ListToCSVWithHeaders(dictList, 'output.csv', WANTED_HEADERS)

これは、実行時に受け取るエラーです。

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 7: ordinal not in range(128)
4

1 に答える 1

9

Unicode を CSV に書き込むことはできませんが、たまたま UTF-8 (または Latin-1、またはその他のほぼすべてのエンコーディング*) エンコーディング Unicode であるバイトを書き込むことはできます。ドキュメントは明示的にこれを言い、それに対処する方法を提案します:

注:このバージョンのcsvモジュールは Unicode 入力をサポートしていません。また、現在、ASCII NUL 文字に関していくつかの問題があります。したがって、すべての入力は安全のために UTF-8 または印刷可能な ASCII にする必要があります。セクション例の例を参照してください。これらの制限は、将来的に削除される予定です。

例のセクションでは、これに対処する方法を示し、unicodeオブジェクトの読み取りと書き込みを可能にするラッパーを提供し、UTF-8 を自動的にエンコード/デコードします。別の文字セットを使用している場合 (たとえば、cp1252 でエンコードされた CSV を必要とする Excel VBscript にこれを渡す予定があるため)、必要に'utf-8'応じて置き換えてください。


サンプルコードは、csvモジュール自体が UTF-8 のみを処理する必要があることを確認するために、いくつかの派手なフットワークを実行しますが、ファイルは別のコーデックである可能性があります。これは、csv モジュールを混乱させる可能性のあるコーデックに対処する優れた方法です。しかし、Latin-1 (または cp1252 のような Latin-1 拡張文字セット)、または UTF-8 自体を探しているだけのようです。その場合、次のようなクイック&ダーティソリューションを使用できます。

w.writerows(mydata)

…次のようなハッキーなことをすることができます:

def utf8ify(d):
    return dict((k.encode('utf-8'), v.encode('utf-8')) for k, v in d.iteritems())

w.writerows(utf8ify(d))

書き込もうとしている値によっては、上記を変更する必要がある場合があります。たとえば、元の dict に Latin-1 文字列がある場合、次のようなものが必要になります。

k.decode('latin-1').encode('utf-8'), …

何を書こうとしているのかわからない場合は…まあ、手っ取り早い解決策を実行することはできません。


編集したバージョンでは、次のように quick&dirty ソリューションを使用しています。

def utf8ify(d):
  return dict((str(k).encode('utf-8'), str(v).encode('utf-8')) for k, v in d.iteritems())

…そして、あなたが渡している値は、のような文字列と、のようなUTF-8でエンコードされたバイト文字列であると私が思うものの混合のunicodeようにu'\xd3'見えます。そこにいくつかの数字か何かがあるかもしれません。あるいは、単に注意しているだけかもしれません。str'Blah \xc3\x93 D\xc3\xa1blah'

とにかく、それはうまくいきません。UTF-8 でエンコードされた文字列は変更されずに通過しstr、 としてデコードされsys.getdefaultencoding()、UTF-8 として再エンコードされますが、Unicode 文字列はデフォルトのエンコードでエンコードされ、デフォルトのエンコードでデコードされ、UTF-8 で再エンコードされます。

これが実際のデータである場合、コードは次のようになります。

def utf8ify_s(s):
    if isinstance(s, unicode):
        return s.encode('utf-8')
    else:
        return str(s)

それは文字列をエンコードし、文字列がすでにUTF-8であるunicodeと仮定してそれらを渡し(変更されないままになります)、呼び出して数値などを文字列に変換します(これは組み込み型で問題なく、カスタムである限り)あなたが書く型は純粋なASCIIまたはUTF-8であり、それらにも問題ありません)。次に、 for eachおよびの代わりに、次の関数を呼び出します。strstrstrstrstr(…).encode('utf-8')kv

def utf8ify(d):
    return dict(utf8ify_s(k): utf8ify_s(v) for k, v in d.iteritems())

それまでの間、コードが機能するようになるまでハッキングしようとするのではなく、ここで実際に何が起こっているのかを理解するために、 Unicode HOWTOやその他必要なものをすべて読むことを強くお勧めします。


* 実際のルールは次のようなものです: NUL バイトが埋め込まれていない (UTF-16 が廃止された)、複数の行にまたがる永続的な状態が存在しない (一部の東アジアのエンコーディングが廃止された)、「代理」スタイルの部分文字がない引用文字のバイトのように一致するバイト。よくわからない場合は、高度なコンバーターを使用して UTF-8 を使用してください。

于 2013-08-05T23:50:41.137 に答える