如何迭代Python 3中的Unicode字符?

我需要一次一个字符地浏览一个Python字符串,但是一个简单的“for”循环会给我一个UTF-16代码单元:

str = "abcu20acU00010302U0010fffd"
for ch in str:
    code = ord(ch)
    print("U+{:04X}".format(code))

打印:

U+0061
U+0062
U+0063
U+20AC
U+D800
U+DF02
U+DBFF
U+DFFD

当我想要的是:

U+0061
U+0062
U+0063
U+20AC
U+10302
U+10FFFD

有没有什么办法可以让Python给我一系列的Unicode代码点,而不管字符串是如何在引擎盖下实际编码的? 我在这里测试Windows,但我需要能够在任何地方工作的代码。 它只需要在Python 3上工作,我不关心Python 2.x.

到目前为止我所能想到的最好的是:

import codecs
str = "abcu20acU00010302U0010fffd"
bytestr, _ = codecs.getencoder("utf_32_be")(str)
for i in range(0, len(bytestr), 4):
    code = 0
    for b in bytestr[i:i + 4]:
        code = (code << 8) + b
    print("U+{:04X}".format(code))

但我希望有一个更简单的方法。

(对于精确的Unicode术语来说,迂腐的挑剔会在头部被无情地殴打,我想我已经明确了我在这之后的情况,请不要浪费空间,“但UTF-16是在技​​术上来说,Unicode也是“有种论点。”


在狭窄的Unicode版本的Python 3.2.1上:

PythonWin 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32.
Portions Copyright 1994-2008 Mark Hammond - see 'Help/About PythonWin' for further copyright information.
>>> import sys
>>> sys.maxunicode
65535

你发现了什么(UTF-16编码):

>>> s = "abcu20acU00010302U0010fffd"
>>> len(s)
8
>>> for c in s:
...     print('U+{:04X}'.format(ord(c)))
...     
U+0061
U+0062
U+0063
U+20AC
U+D800
U+DF02
U+DBFF
U+DFFD

解决方法:

>>> import struct
>>> s=s.encode('utf-32-be')
>>> struct.unpack('>{}L'.format(len(s)//4),s)
(97, 98, 99, 8364, 66306, 1114109)
>>> for i in struct.unpack('>{}L'.format(len(s)//4),s):
...     print('U+{:04X}'.format(i))
...     
U+0061
U+0062
U+0063
U+20AC
U+10302
U+10FFFD

Python 3.3的更新:

现在它按照OP的预期工作:

>>> s = "abcu20acU00010302U0010fffd"
>>> len(s)
6
>>> for c in s:
...     print('U+{:04X}'.format(ord(c)))
...     
U+0061
U+0062
U+0063
U+20AC
U+10302
U+10FFFD

Python通常在内部将unicode值存储为UCS2。 UTF-32 U00010302字符的UTF-16表示是 UD800 UDF02,这就是您获得该结果的原因。

也就是说,有一些使用UCS4的python版本,但是这些版本并不相互兼容。

看看这里。

Py_UNICODE这种类型表示Python内部用作保存Unicode序号的基础的存储类型。 Python的默认构建对Py_UNICODE使用16位类型,并将Unicode值在内部存储为UCS2。 也可以构建一个UCS4版本的Python(最新的Linux发行版带有UCS4版本的Python)。 然后,这些构建对Py_UNICODE使用32位类型,并将Unicode数据内部存储为UCS4。 在wchar_t可用且与所选Python Unicode构建变体兼容的平台上,Py_UNICODE是wchar_t的typedef别名,以增强本机平台的兼容性。 在所有其他平台上,Py_UNICODE是unsigned short(UCS2)或unsigned long(UCS4)的typedef别名。


如果将该字符串创建为一个unicode对象,它应该能够自动一次中断一个字符。 例如:

Python 2.6:

s = u"abcu20acU00010302U0010fffd"   # note u in front!
for c in s:
    print "U+%04x" % ord(c)

我收到了:

U+0061
U+0062
U+0063
U+20ac
U+10302
U+10fffd

Python 3.2:

s = "abcu20acU00010302U0010fffd"
for c in s:
    print ("U+%04x" % ord(c))

它为我工作:

U+0061
U+0062
U+0063
U+20ac
U+10302
U+10fffd

此外,我发现这个链接解释说,行为正常工作。 如果字符串来自文件等,它可能需要首先解码。

更新

我在这里找到了一个有见地的解释。 内部Unicode表示大小是一个编译时选项,如果在16位平面以外使用“宽”字符,则需要自行构建python以删除限制,或者使用此页面上的其中一种解决方法。 显然,很多Linux发行版都已经为我们做了这些,就像我上面遇到的一样。

链接地址: http://www.djcxy.com/p/54045.html

上一篇: How to iterate over Unicode characters in Python 3?

下一篇: with/without SuppressFinalize in Dispose