#byteslice 无法正确计算包含重音符号的字符串中的字节
#byteslice fails in correctly calculating the bytes in a string including accents
我注意到,当给定的字符串包含重音符号时,byteslice
方法无法 return 正确的字符,因为 Ruby 显然会计算字符串中的字节数奇怪的方式。那个,或者我遗漏了一些关于如何计算字符串中的字节的信息。
这是一个 MWE:
text = "Càdiz"
puts text.byteslice(0)
puts text.byteslice(1)
puts text.byteslice(2)
puts text.byteslice(3)
puts text.byteslice(4)
我在终端得到的结果是这样的:
C
�
�
d
i
当然,那个带有重音符号的字母给我带来了麻烦。这是正常的吗?有没有办法使用 text.byteslice(4)
或其他类似方法始终获取 return 中字符串的第五个字符?
I noticed that, when a given string includes accents, the byteslice
method fails in returning the correct character,
String#byteslice returns bytes 不是字符,所以它在 return 中失败的事实不应该是非常令人惊讶。
since Ruby apparently counts the bytes in the string in some weird way.
我看不出有什么奇怪的。您要求它 return 第二个字节,它 return 编辑了第二个字节。 UTF-8 专门设计 multi-byte 序列的每个字节都是非法编码,所以如果你像这样拆分一个字符,它 不可能导致合法字符。
即设计,它允许 UTF-8 流解码器self-synchronize。
Is there a way to always get the fifth character of a string in return, using text.byteslice(4)
or some other similar method?
不,无法使用 byteslice
获取第五个 字符 ,因为 byteslice
用于 字节 ,不是个字符。
您 可以 ,但是,使用 String#[]
:
获取第五个字符
text[4]
但是,你可能想要的不是第五个字符,而是第五个字素群,你可以使用 String#grapheme_clusters
方法来做到这一点:
text.grapheme_clusters[4]
让我们看看我的名字,例如。在 Unicode 中有两种不同的方式来写我的名字:
- J ö r g
- J o <组合字符分音符> r g
请注意,第一个版本的长度为 4 个字符,第二个版本的长度为 5 个字符。不过,两者都有四个 字素簇 。但是,请注意,虽然 字形 相同,但字素簇不同。
第一个版本在ISO8859-15中编码为4个字节,但在UTF-8中需要5个字节,在UTF-16中需要8个字节,在UTF-32中需要16个字节.
第二个版本不能用ISO8859-15编码,因为ISO8859-15没有组合字符分音符。事实上,它根本没有组合字符。以 UTF-8 编码第二个版本需要 6 个字节,UTF-16 需要 10 个字节,UTF-32 需要 20 个字节。
让我们假设第二个例子,假设它是用 UTF-8 编码的。该字符串由以下个字符组成:
- U+004A 拉丁文大写字母 J
- U+006F 拉丁文小写字母 o
- U+0308 组合字符分音符
- U+0072 拉丁文小写字母 r
- U+0067 拉丁文小写字母 g
这些字节序列被编码为 UTF-8:
0x4A
0x6F
0xCC
0x88
0x72
0x67
因此,例如,如果您要请求第三个 字节,您将得到 0xCC
。如果您尝试将其显示为字符串,它将失败,因为 0xCC
本身不是合法的 UTF-8 编码。它是 multi-byte 序列的第一个字节。
如果您要求第三个 字符,您将得到 U+0308 组合字符分音符。如果您尝试将其显示为字符串,它将失败,因为没有基本字符的组合字符没有意义。
如果你要求第三个 字形簇,你会得到 'r'
,这 可能想要。
所以,简而言之:你没有得到你想要的字符的原因是你没有要求一个字符,你要求一个字节。如果你想要一个字符,你需要一个字符。但是,您可能想要的是字素簇,而不是字符。
事实上,字素簇几乎总是您想要的。
我注意到,当给定的字符串包含重音符号时,byteslice
方法无法 return 正确的字符,因为 Ruby 显然会计算字符串中的字节数奇怪的方式。那个,或者我遗漏了一些关于如何计算字符串中的字节的信息。
这是一个 MWE:
text = "Càdiz"
puts text.byteslice(0)
puts text.byteslice(1)
puts text.byteslice(2)
puts text.byteslice(3)
puts text.byteslice(4)
我在终端得到的结果是这样的:
C
�
�
d
i
当然,那个带有重音符号的字母给我带来了麻烦。这是正常的吗?有没有办法使用 text.byteslice(4)
或其他类似方法始终获取 return 中字符串的第五个字符?
I noticed that, when a given string includes accents, the
byteslice
method fails in returning the correct character,
String#byteslice returns bytes 不是字符,所以它在 return 中失败的事实不应该是非常令人惊讶。
since Ruby apparently counts the bytes in the string in some weird way.
我看不出有什么奇怪的。您要求它 return 第二个字节,它 return 编辑了第二个字节。 UTF-8 专门设计 multi-byte 序列的每个字节都是非法编码,所以如果你像这样拆分一个字符,它 不可能导致合法字符。
即设计,它允许 UTF-8 流解码器self-synchronize。
Is there a way to always get the fifth character of a string in return, using
text.byteslice(4)
or some other similar method?
不,无法使用 byteslice
获取第五个 字符 ,因为 byteslice
用于 字节 ,不是个字符。
您 可以 ,但是,使用 String#[]
:
text[4]
但是,你可能想要的不是第五个字符,而是第五个字素群,你可以使用 String#grapheme_clusters
方法来做到这一点:
text.grapheme_clusters[4]
让我们看看我的名字,例如。在 Unicode 中有两种不同的方式来写我的名字:
- J ö r g
- J o <组合字符分音符> r g
请注意,第一个版本的长度为 4 个字符,第二个版本的长度为 5 个字符。不过,两者都有四个 字素簇 。但是,请注意,虽然 字形 相同,但字素簇不同。
第一个版本在ISO8859-15中编码为4个字节,但在UTF-8中需要5个字节,在UTF-16中需要8个字节,在UTF-32中需要16个字节.
第二个版本不能用ISO8859-15编码,因为ISO8859-15没有组合字符分音符。事实上,它根本没有组合字符。以 UTF-8 编码第二个版本需要 6 个字节,UTF-16 需要 10 个字节,UTF-32 需要 20 个字节。
让我们假设第二个例子,假设它是用 UTF-8 编码的。该字符串由以下个字符组成:
- U+004A 拉丁文大写字母 J
- U+006F 拉丁文小写字母 o
- U+0308 组合字符分音符
- U+0072 拉丁文小写字母 r
- U+0067 拉丁文小写字母 g
这些字节序列被编码为 UTF-8:
0x4A
0x6F
0xCC
0x88
0x72
0x67
因此,例如,如果您要请求第三个 字节,您将得到 0xCC
。如果您尝试将其显示为字符串,它将失败,因为 0xCC
本身不是合法的 UTF-8 编码。它是 multi-byte 序列的第一个字节。
如果您要求第三个 字符,您将得到 U+0308 组合字符分音符。如果您尝试将其显示为字符串,它将失败,因为没有基本字符的组合字符没有意义。
如果你要求第三个 字形簇,你会得到 'r'
,这 可能想要。
所以,简而言之:你没有得到你想要的字符的原因是你没有要求一个字符,你要求一个字节。如果你想要一个字符,你需要一个字符。但是,您可能想要的是字素簇,而不是字符。
事实上,字素簇几乎总是您想要的。