Python:通过utf-8字符串迭代 - >迭代器的数据类型/编码是什么?

时间:2015-02-20 16:02:50

标签: python encoding utf-8 character-encoding

我有一个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]])

2 个答案:

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