#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 中有两种不同的方式来写我的名字:

  1. J ö r g
  2. 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 编码的。该字符串由以下个字符组成:

  1. U+004A 拉丁文大写字母 J
  2. U+006F 拉丁文小写字母 o
  3. U+0308 组合字符分音符
  4. U+0072 拉丁文小写字母 r
  5. U+0067 拉丁文小写字母 g

这些字节序列被编码为 UTF-8:

  1. 0x4A
  2. 0x6F
  3. 0xCC
  4. 0x88
  5. 0x72
  6. 0x67

因此,例如,如果您要请求第三个 字节,您将得到 0xCC。如果您尝试将其显示为字符串,它将失败,因为 0xCC 本身不是合法的 UTF-8 编码。它是 multi-byte 序列的第一个字节。

如果您要求第三个 字符,您将得到 U+0308 组合字符分音符。如果您尝试将其显示为字符串,它将失败,因为没有基本字符的组合字符没有意义。

如果你要求第三个 字形簇,你会得到 'r',这 可能想要。

所以,简而言之:你没有得到你想要的字符的原因是你没有要求一个字符,你要求一个字节。如果你想要一个字符,你需要一个字符。但是,您可能想要的是字素簇,而不是字符。

事实上,字素簇几乎总是您想要的。