【问题标题】:How can I write special characters to a CSV in Python?如何在 Python 中将特殊字符写入 CSV?
【发布时间】:2013-08-05 23:23:33
【问题描述】:

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

【问题讨论】:

  • 请不要只发布没有回溯的错误消息。您的代码中至少有三个不同的行可能来自该错误,也许更多;如果我不知道它实际上来自哪一个,那么很难调试它。 (另外,当您使用您发布的示例数据运行您发布的代码时,您真的会遇到该错误吗?)

标签: python


【解决方案1】:

您不能将 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 这样的 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,它们也可以)。然后,对于每个kv,而不是str(…).encode('utf-8'),调用这个函数:

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

同时,我强烈建议您通读 Unicode HOWTO 以及您需要的任何其他内容,以了解此处实际发生的情况,而不是仅仅尝试破解您的代码直到它似乎可以工作。


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

【讨论】:

  • 尝试此操作时,我收到错误“UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 7: ordinal not in range(128)”
  • @JStew:这样的评论对调试完全没用。特别是在回答提出两个或更多解决方案的答案时,“尝试这个”并没有告诉我们您尝试了什么。即使答案只有一个解决方案,它仍然不会告诉我们您的确切代码。没有回溯的错误,或者至少是它来自的代码行,也是没有用的。
  • 我已经用我尝试过的方法编辑了我原来的问题。在运行脚本之前,我还尝试将我的 CSV 保存为 UTF-8。
  • @abc 没有;如答案中所述,当您尝试存储已经编码为str而不是unicode的值,或者是数字或字符串以外的其他东西,或者是混合或以上两项或多项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-26
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-31
相关资源
最近更新 更多