0

我正在尝试在 Python 中模仿 Rubys .bytesize 字符串函数。但是我遇到了某些字符的问题,例如“'”

在红宝石中

"‘".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 相同的结果。

4

2 回答 2

1

您正在处理 UTF-8 字符串,忘记字节。

String#codepoints将返回代码点数组,String#length返回 UTF-8 字符串的长度:

"‘".codepoints #⇒ [8216]
"‘".length     #⇒ 1

String#unpack提供对字形的低级访问。

"‘".unpack "U+"

无论您是否仍想访问bytes,您都可以:

"‘".unpack "C*"
#⇒ [226, 128, 152]

要在 python 中获取 UTF-8 符号的字节,可以使用bytes

>>> chars = bytes("‘".encode("utf8"))
>>> chars
#⇒ b'\xe2\x80\x98'
>>> len(chars)
#⇒ 3
于 2017-07-17T15:20:42.413 回答
0

假设你有两个 UTF-8 字符串

蟒蛇 3:

>>> s1
'è'
>>> s2
'è'

和红宝石:

> s1
=> "è"
> s2
=> "è"

虽然这些字符串看起来都一样,但它们不是:

>>> s1==s2
False

> s1==s2
=> false

这是因为虽然它是同一个字,但它们实际上是两个不同的字节字符串:

>>> [[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", "̀"]]] 

正如你所看到的,有多种方法可以组成一个字素,或者我们所说的字符。如果您有组合字符,例如'̀'in s2,则字符串的长度(以字节为单位)将与您预期的不同:

> s1.length==s2.length
=> false

在 Ruby 中,您可以使用\X正则表达式扫描组成单个字素的字节或字节组:

> s2.scan(/\X/)
=> ["è"]

那么它们的逻辑长度将是相同的:

> s1.scan(/\X/).length==s2.scan(/\X/).length
=> true

或者,规范化字符串:

您还可以在 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. (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
于 2017-07-17T17:48:57.967 回答