我试图在Python中模仿Rubys .bytesize字符串函数。但是我遇到某些字符的问题,例如"“"
在Ruby中
"‘".bytesize returns 3
"‘".bytes returns [226, 128, 152]
在Python中
ord("‘") returns 8216
len(ord("‘")) returns 1
两种语言之间的编码有何不同?这进一步与不同的在线转换器混淆,提供类似的对比结果。例如 - http://www.unit-conversion.info/texttools/ascii/产生与Ruby相同的结果,而https://www.branah.com/ascii-converter产生与Python相同的结果。
答案 0 :(得分:1)
您正在处理UTF-8字符串,忘记字节。
String#codepoints
将返回代码点数组,String#length
返回UTF-8字符串的长度:
"‘".codepoints #⇒ [8216]
"‘".length #⇒ 1
String#unpack
提供对字形表的低级访问。
"‘".unpack "U+"
您是否仍希望获得对字节的访问权限,您可以:
"‘".unpack "C*"
#⇒ [226, 128, 152]
要在python中获取UTF-8符号的字节,可以使用bytes
:
>>> chars = bytes("‘".encode("utf8"))
>>> chars
#⇒ b'\xe2\x80\x98'
>>> len(chars)
#⇒ 3
答案 1 :(得分:0)
假设您在
中有两个UTF-8字符串Python 3:
>>> s1
'è'
>>> s2
'è'
和Ruby:
> s1
=> "è"
> s2
=> "è"
虽然这些字符串的所有看起来都是,但它们不是:
>>> s1==s2
False
> s1==s2
=> false
这是因为虽然它是相同的grapheme,但它们实际上是两个不同的字节串:
>>> [[s, len(s), list(s)] for s in (s1,s2)]
[['è', 1, ['è']], ['è', 2, ['e', '̀']]]
> [s1,s2].map {|s| [s, s.length, s.each_char.to_a]}
=> [["è", 1, ["è"]], ["è", 2, ["e", "̀"]]]
正如您所看到的,有多种方法可以构成单个字形,或者我们称之为字符的方式。如果您有'̀'
中的s2
等撰写字符,则字符串的长度(以字节为单位)将与您预期的不同:
> s1.length==s2.length
=> false
在Ruby中,您可以使用\X
正则表达式扫描构成单个字素的字节或字节组:
> s2.scan(/\X/)
=> ["è"]
然后他们的逻辑长度将是相同的:
> s1.scan(/\X/).length==s2.scan(/\X/).length
=> true
或者,规范化字符串:
您还可以normalize Ruby中的s2字符串将两个字节合并为一个等效的字形:
> s2.unicode_normalize.length==s1.length
=> true
在Python中,您可以使用unicodedate
进行规范化:
>>> import unicodedata
>>> unicodedata.normalize('NFC', s2) == s1
True
>>> len(unicodedata.normalize('NFC', s2)) == len(s1)
True
或者安装并使用支持\X
的{{3}}。 (re
模块不支持\X
)
如果您真的想要在Python中使用与Ruby相同的逐字节方法,那么您可以这样做:
>>> [int(e) for e in bytes("‘".encode('utf-8'))]
[226, 128, 152]
> "‘".bytes
=> [226, 128, 152]
但我不确定你打算怎么做......
或者,如果您想要相同的ord
值:
>>> ord("‘")
8216
> "‘".ord
=> 8216