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)

......你可以做一些像这样的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'), …

如果你不知道你想写什么样的东西……好吧,你不能做快速&肮脏的解决方案。


在您编辑的版本中,您以这种方式使用 quick&dirty 解决方案:

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字节字符串,例如'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)

这将对unicode字符串进行编码,假设str字符串已经在 UTF-8 中并传递它们str(这将使它们保持不变),并通过调用将数字等转换为字符串str(这对于任何内置类型都很好,只要自定义您编写的类型str是纯 ASCII 或 UTF-8,它们也可以)。然后,而不是str(…).encode('utf-8')每个kand v,调用这个函数:

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 回答