我有一个utf-8编码的字符串(主要是中文+一些英文),并希望对它们进行字母计数。 (类似于英文单词计数)。
所以我用了
for letter in text: # text is a utf-8 encoded str
但我不确定我收到的是什么字母'。 '文本'在屏幕上打印很好并写入csv罚款。但是这封信'在'中用于文字中的字母'看起来在屏幕和csv文件中都崩溃了。我认为这肯定是与编码相关的一些问题,但在这里添加.encode('utf-8')
并没有解决问题并返回错误,如
UnicodeDecodeError: 'ascii' codec can't decode byte 0x83 in position 0: ordinal not in range(128)
我的意思是下面的代码没有返回错误,但字母看起来全部崩溃了,当我添加.encode(' utf-8')时它返回上面的错误信息
打印letter.encode('utf-8')
或wcwriter.writerows([[k.encode('utf-8'), v]])
# -*- coding: utf-8 -*-
...
with open(fname+'.csv', 'wb') as twfile:
twwriter = csv.writer(twfile)
twwriter.writerows([[u'Date/Time', u'Text', u'ID', u'Location', u'City', u'Province']])
for statuses in jres.get('statuses'): # jres is a json format response returned from a API call request
text = statuses.get('text').encode('utf-8')
if keyword in text:
td = statuses.get('created_at').encode('utf-8')
name = statuses.get('user').get('screen_name').encode('utf-8')
loc = statuses.get('user').get('location').encode('utf-8')
city = statuses.get('user').get('city').encode('utf-8')
province = statuses.get('user').get('province').encode('utf-8')
twwriter.writerows([[td, text, name, loc, city, province]])
keycount += 1
# this is the problematic part. I'm not sure exactly what data type or encoding I'm getting for 'letter' below
for letter in text:
if letter not in dismiss:
print letter # this will print a lot of crushed letters
if letter not in wordcount:
wordcount[letter] = 1
else:
wordcount[letter] += 1
with open(wcname+'.csv', 'wb') as wcfile:
wcwriter = csv.writer(wcfile)
wcwriter.writerows([[u'Letter', u'Number']])
for k, v in wordcount.items():
wcwriter.writerows([[k, v]])
答案 0 :(得分:3)
UTF-8编码的字节可以在屏幕上正常打印或写入文件,但这仅仅是因为您的屏幕(终端或控制台)和阅读文件的任何内容都能理解UTF-8。
UTF-8编码每个代码点使用一个或多个字节。迭代不是通过代码点逐步遍历数据代码点,而是逐字节。因此,字符'å'
被编码为UTF8为两个字节,C3和A5。试图将这两个字节作为字母来处理会产生问题:
>>> 'å'
'\xc3\xa5'
>>> for byte in 'å':
... print repr(byte)
...
'\xc3'
'\xa5'
您应该解码到unicode
值,以便Python知道由字节编码的代码点,或者您已经拥有Unicode的地方,不编码:
>>> for codepoint in 'å'.decode('utf8'):
... print repr(codepoint), codepoint
...
u'\xe5' å
当您尝试编码已编码的字节时,会导致异常。 Python首先将字节解码为Unicode以使其符合并编码回字节,但尝试使用默认的ASCII编码。这就是为什么在尝试使用UnicodeDecodeError
时会得到Decode
(请注意encode()
):
>>> 'å'.encode('utf8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
一般来说,您希望尽可能将文本视为Unicode。实现Unicode 三明治,尽可能早地从字节解码到Unicode,并且只在您将数据写回文件时进行编码,尽可能晚。您正在处理的JSON数据已经是Unicode,因此您只需在生成CSV行时编码为UTF8,但不早于。
在这种情况下,这意味着您应不编码text
:
for statuses in jres.get('statuses'): # jres is a json format response returned from a API call request
text = statuses['text']
而只是在将它传递给CSV编写器时对其进行编码:
twwriter.writerows([[td, text.encode('utf8'), name, loc, city, province]])
你可能想研究一下Unicode和编码之间的区别,以及它与Python的关系:
答案 1 :(得分:0)
即使使用解码的utf-8,Python似乎也将emojis等分成多个代码点。我使用以下函数来解决这个问题:
# ustr must be "decoded" unicode string, e.g. u""
def each_utf8_char(ustr, pointer=0):
ustr = ustr.encode('utf-8')
slen = len(ustr)
char = ustr[pointer] if slen > pointer else False
while char:
charVal = ord(char)
if charVal < 128:
bytes = 1
elif charVal < 224:
bytes = 2
elif charVal < 240:
bytes = 3
elif charVal < 248:
bytes = 4
elif charVal == 252:
bytes = 5
else:
bytes = 6
yield ustr[pointer:pointer+bytes].decode('utf-8')
pointer += bytes
char = ustr[pointer] if slen > pointer else False
它是一个生成器,所以你可以这样使用它:
my_ustr = u' Cheers!'
for char in each_utf8_char(my_ustr):
print char