如何在Python中将特殊字符写入CSV?

时间:2013-08-05 23:23:34

标签: python

尝试在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)

1 个答案:

答案 0 :(得分:8)

您不能将Unicode写入CSV ...但您可以编写恰好是UTF-8(或Latin-1,或几乎任何其他编码*)编码Unicode的字节。 The docs明确说出这一点,并建议如何处理它:

  

注意:此版本的csv模块不支持Unicode输入。此外,目前有一些关于ASCII NUL字符的问题。因此,所有输入应为UTF-8或可打印的ASCII以确保安全;请参阅示例部分中的示例。这些限制将在未来删除。

Examples section显示如何处理此问题,提供包装器,让您自动读取和写入unicode个对象,自动编码/解码UTF-8。如果您正在使用不同的字符集(例如,因为您计划将其传递给需要cp1252编码的CSV的Excel VBscript),请根据需要替换'utf-8'


示例代码做了一些花哨的步法,以确保csv模块本身只需要处理UTF-8,而文件可以在不同的编解码器中。这是处理可能会混淆csv模块的编解码器的好方法。但看起来你只是在寻找Latin-1(或者像cp1252这样的拉丁语1扩展字符集),甚至可能是UTF-8本身。在这种情况下,您可以使用快速和肮脏的解决方案,如下所示:

w.writerows(mydata)

...你可以像这样做一些hacky:

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

w.writerows(utf8ify(d))

根据您尝试编写的值,您可能需要更改上述内容。例如,如果原始字典中有Latin-1字符串,则需要以下内容:

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

如果你不知道你想写的那种东西......好吧,你不能做快速和彻底的解决方案。


在您编辑的版本中,您正在以这种方式使用快速和肮脏的解决方案:

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

...您传递的值似乎是unicode字符串的混合,如u'\xd3'和我认为是UTF-8编码的str字节字符串,如{{1} }。那里可能还有一些数字或某些东西,或者你可能只是小心。

无论如何,那是行不通的; UTF-8编码的字符串将通过'Blah \xc3\x93 D\xc3\xa1blah'未更改,解码为str,并重新编码为UTF-8,而Unicode字符串将使用默认编码进行编码,使用默认编码进行解码,并用UTF-8重新编码。

如果这是您的实际数据,代码将是这样的:

sys.getdefaultencoding()

这将编码def utf8ify_s(s): if isinstance(s, unicode): return s.encode('utf-8') else: return str(s) 字符串,假设unicode字符串已经是UTF-8并通过str传递(这将使它们保持不变),并将数字等转换为字符串通过调用str(对于任何内置类型都可以,并且只要您编写的自定义类型'str是纯ASCII或UTF-8,它们也适合它们)。然后,对每个strstr(…).encode('utf-8')而不是k,请调用此函数:

v

与此同时,我强烈建议您仔细阅读Unicode HOWTO以及您需要的任何其他内容,以了解其中的实际内容,而不仅仅是尝试破解代码,直到它看起来有效。< / p>


*实际规则是这样的:没有嵌入的NUL字节(因此UTF-16已经出局),没有可以跨越多行的持久状态(因此一些东亚编码已经出局),并且没有“代理”式与引号字符的字节匹配的部分字符字节。如果您不确定...使用花式转换器并通过UTF-8。