我需要一次遍历Python字符串一个字符,但是一个简单的“for”循环代替了UTF-16代码单元:
str = "abc\u20ac\U00010302\U0010fffd"
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 = "abc\u20ac\U00010302\U0010fffd"
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“的一种参数。”
答案 0 :(得分:7)
在Python 3.2.1上使用窄版本的Unicode:
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 = "abc\u20ac\U00010302\U0010fffd"
>>> 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
现在它按照OP期望的方式工作:
>>> s = "abc\u20ac\U00010302\U0010fffd"
>>> 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
答案 1 :(得分:3)
Python通常在内部将unicode值存储为UCS2。 UTF-32 \ U00010302字符的UTF-16表示是\ UD800 \ UDF02,这就是你得到这个结果的原因。
也就是说,有一些使用UCS4的python构建,但这些构建彼此不兼容。
看看here。
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别名。
答案 2 :(得分:3)
如果您将字符串创建为unicode对象,它应该能够自动中断一个字符。 E.g:
Python 2.6:
s = u"abc\u20ac\U00010302\U0010fffd" # 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 = "abc\u20ac\U00010302\U0010fffd"
for c in s:
print ("U+%04x" % ord(c))
它对我有用:
U+0061
U+0062
U+0063
U+20ac
U+10302
U+10fffd
此外,我发现this link解释说行为正常。如果字符串来自文件等,则可能需要先解码。
<强>更新强>:
我找到了一个富有洞察力的explanation here。内部Unicode表示大小是编译时选项,如果使用16位平面之外的“宽”字符,则需要自己构建python以消除限制,或者使用此页面上的解决方法之一。显然很多Linux发行版已经像我上面遇到的那样为你做了这个。