我有一个csv文件(参见下面的[1]),其中包含非ascii文本(例如Antonio Melé
之类的名称。该文件包含带有URL,摘录和注释的书籍列表。
在Python 3.5中,我打开并处理文件:
# -*- coding: utf-8 -*-
import codecs
import csv
import pdb
def select_book_matching_keyword(books, kw):
"""
Will select the csv rows for which any column has matching keyword in it
Snippet from csv file:
`Django By Example,Antonio Melé,Using class-based ...`
`Antonio Melé`
becomes
`b'Antonio Mel\xc3\xa9'`
"""
selected_books = []
for book in books:
kw_in_any_column = [column for column in book if kw in column.decode()]
# >> Without the `column.decode()` above I cannot
# run this list comprehension (that is if I
# write `if kw in column` instead of `if kw in column.decode()
if kw_in_any_column:
# print(book)
selected_books.append(book)
return selected_books
if __name__=='__main__':
f = codecs.open('safari-annotations-export-3.csv', 'r', 'utf-8')
reader = csv.reader(f)
books = []
for row in reader:
book_utf8 = [column.encode("utf-8") for column in row]
books.append(book_utf8)
print(book_utf8)
pdb.set_trace()
现在打印csv的行(参见上面的print(book_utf8)
)将给出如下结果:
[b'Django By Example', b'Antonio Mel\xc3\xa9', b'Using class-based views', b'2017-03-08', b'https://www.safaribooksonline.com/library/view/django-by-example/9781784391911/', b'https://www.safaribooksonline.com/library/view/django-by-example/9781784391911/ch01s09.html', b'https://www.safaribooksonline.com/a/django-by-example/5869158/', b'Using class-based views', b'']
首先,我有一个字节前缀。为什么? (Python 3.x默认情况下将字符串视为unicode,默认情况下Python 2.7将其视为字节。)
然后我有这个:b'Antonio Mel\xc3\xa9'
而不是Antonio Melé
。
我知道我还没有完全掌握Python中的编码概念。在SO上阅读了很多帖子,但我仍然没有得到它。
utf-8
?我做到了。[2]尝试打印csv文件的行列表而不对行的列进行编码会给我一个错误:
(snip)
['Learning jQuery Deferreds', 'Terry Jones...', '2. The jQuery Deferred API', '2017-04-06', 'https://www.safaribooksonline.com/library/view/learning-jquery-deferreds/9781449369385/', 'https://www.safaribooksonline.com/library/view/learning-jquery-deferreds/9781449369385/ch02.html', 'https://www.safaribooksonline.com/a/learning-jquery-deferreds/6635517/', 'More Terminology: Resolve, Reject and Progress', '']
*** UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 368: ordinal not in range(128)
答案 0 :(得分:2)
通常,在与外部世界通信时完成所有编码/解码。在您的示例中,有两个通信步骤:
codecs.open()
,print()
内置函数写出结果。在此之间,你应该总是使用解码的字符串,即。输入str
(Python 2' s unicode
)。
第一点很顺利,最初:您使用正确的编码打开文件,让csv
进行格式解析。
这可以确保将磁盘上找到的字节正确解码为字符串,而无需使用decode
方法。
(作为旁注,你可以在这里省略codecs
并使用内置的open(filename, 'r', encoding='utf-8')
,但它实际上也做同样的事情。)
但是,然后,您使用以下行重新编码字符串:
book_utf8 = [column.encode("utf-8") for column in row]
你不应该这样做。现在你必须处理bytes
而不是字符串。
注意:
>>> 'Antonio Melé'.encode('utf-8')
b'Antonio Mel\xc3\xa9'
bytes
类型具有字符串的共同特征,但它们不兼容。
这就是为什么你必须在decode
函数中使用select_book_matching_keyword
每个元素的原因(在你的代码片段中没有使用,顺便说一句。),以便在字符串和字符串之间进行成员资格测试,不是字符串和字节。
这两种类型之间的区别之一是print()
使用repr
表单来显示bytes
,因此输出将包含引号和b
前缀:< / p>
>>> print(b'Antonio Mel\xc3\xa9')
b'Antonio Mel\xc3\xa9'
与打印字符串比较:
>>> print('Antonio Melé')
Antonio Melé
这将我们带到下一个问题:使用print()
将数据写入STDOUT。
如果您尝试上述行,您可能会遇到异常:
>>> print('Antonio Melé')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' in position 11: ordinal not in range(128)
问题在于,显然使用了'ascii'
编码。
现在,您如何指定编码?
使用open
写入磁盘上的文件时很清楚:
f = open(filename, 'w', encoding='utf8')
f.write('Antonio Melé')
f.close()
但您无法告诉print
要使用的编码。
原因是它使用已经打开的文件句柄,即。 sys.stdout
。就我而言,这是:
>>> sys.stdout
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
但您可能会看到encoding='ascii'
或类似'ANSI_X3.4-1968'
的内容。
您有两种可能性:
print
。sys.stdout
的编码。我希望第一种可能性是显而易见的。
对于第二行,您需要一行额外的代码(假设导入了sys
):
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer)
现在print
将使用UTF-8编码字符串。
但是,您可能仍有问题: 很可能您的终端没有配置为接受并正确显示UTF-8文本,或者它甚至不支持Unicode。 如果是这种情况,您可能会在屏幕上显示乱码,或者可能是另一个例外。 但是这个问题在Python之外,你必须通过终端配置修改它,或者切换到另一个。