我正在阅读 Effective Go documentation 中有关 for
语句的部分并遇到了这个例子:
for pos, char := range "日本\x80語" {
fmt.Printf("Character %#U, at position: %d\n", char, pos)
}
输出是:
Character U+65E5 '日', at position: 0
Character U+672C '本', at position: 3
Character U+FFFD '�', at position: 6
Character U+8A9E '語', at position: 7
我不明白的是为什么位置是 0、3、6 和 7。这告诉我第一个和第二个字符长 3 个字节,而“替换 rune ”(U+FFFD) 长 1 个字节,我接受并理解。但是,我认为 rune
是 int32
类型,因此每个都是 4 个字节,而不是三个。
为什么范围内的位置与每个值应消耗的内存总量不同?
最佳答案
Go 中的
string
值存储为只读字节 slice ([]byte
),其中字节是 ( rune
的) string
。 UTF-8是一种变长编码,不同的Unicode码位可能使用不同的字节数进行编码。例如,0..127
范围内的值被编码为单个字节(其值是 unicode 代码点本身),但大于 127 的值使用超过 1 个字节。 unicode/utf8
包包含与 UTF-8 相关的实用函数和常量,例如 utf8.UTFMax
报告有效 Unicode 代码点在 UTF-8 编码中可能“占用”的最大字节数(即 4)。
这里要注意一件事:并非所有可能的字节序列都是有效 UTF-8 序列。 string
可以是任何 字节序列,甚至是那些无效的 UTF-8 序列。例如,string
值 "\xff"
表示无效的 UTF-8 字节序列,有关详细信息,请参见 How do I represent an Optional String in Go?
for range
构造——当应用于 string
值时——迭代 string
的 rune :
For a string value, the "range" clause iterates over the Unicode code points in the string starting at byte index 0. On successive iterations, the index value will be the index of the first byte of successive UTF-8-encoded code points in the string, and the second value, of type
rune
, will be the value of the corresponding code point. If the iteration encounters an invalid UTF-8 sequence, the second value will be0xFFFD
, the Unicode replacement character, and the next iteration will advance a single byte in the string.
for range
构造可能会产生 1 或 2 个迭代值。使用 2 时,如您的示例所示:
for pos, char := range "日本\x80語" {
fmt.Printf("Character %#U, at position: %d\n", char, pos)
}
对于每次迭代,pos
将是 rune /字符的字节索引,而char
将是string
的 rune 。正如您在上面的引用中看到的,如果 string
是无效的 UTF-8 字节序列,当遇到无效的 UTF-8 序列时,char
将是 0xFFFD
(Unicode 替换字符)和for range
构造(迭代)将前进一个仅单个字节。
总结一下: 位置始终是当前迭代的 rune
的字节索引(或者更具体地说:第一个字节的字节索引当前迭代的rune
的UTF-8编码序列),但如果遇到无效的UTF-8序列,则位置(索引)只会在下一次迭代中增加1。
如果您想了解有关该主题的更多信息,必读博文:
关于string - 在 UTF-8 字符串中循环时,是什么决定了字符的位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41779147/