在循环UTF-8字符串时,什么决定了角色的位置?

时间:2017-01-21 12:07:52

标签: string for-loop go unicode utf-8

我正在阅读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个字节,'替换符文'(U + FFFD)长1个字节,我接受并理解。但是,我认为rune属于int32类型,因此每个都是4个字节,而不是3个。

为什么范围内的位置与每个值应消耗的内存总量不同?

2 个答案:

答案 0 :(得分:4)

Go中的

string值存储为只读字节片([]byte),其中字节是(rune s){string s的UTF-8编码字节1}}。 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的符文:

  

对于字符串值,"范围"子句从字节索引0开始迭代字符串中的Unicode代码点。在连续迭代中,索引值将是字符串中连续UTF-8编码的代码点的第一个字节的索引,第二个值是类型rune,将是相应代码点的值。 如果迭代遇到无效的UTF-8序列,则第二个值将是0xFFFD,Unicode替换字符,下一次迭代将在字符串中前进一个字节。

for range构造可以产生1或2个迭代值。使用2时,如示例所示:

for pos, char := range "日本\x80語" {
    fmt.Printf("Character %#U, at position: %d\n", char, pos)
}

对于每次迭代,pos将是符文/字符的字节索引,char将是string的符文。正如您在上面的引文中所看到的,如果string是无效的UTF-8字节序列,当遇到无效的UTF-8序列时,char将是0xFFFD(Unicode替换字符),for range构造(迭代)只会推进单个字节

总结一下:该位置始终是当前迭代的rune的字节索引(或更具体地说:UTF-8的第一个字节的字节索引)当前迭代的rune的编码序列,但如果遇到无效的UTF-8序列,则位置(索引)在下一次迭代中只会递增1。

如果您想了解有关该主题的更多信息,请阅读必读博客文章:

The Go Blog: Strings, bytes, runes and characters in Go

答案 1 :(得分:0)

rune是代码点。代码点只是整数。如果您愿意,甚至可以使用int64来存储它。 (但Unicode只有1,114,112个代码点,所以int32应该是正确的选择。难怪rune是Golang中int32的别名。)

不同的编码方案以不同的方式编码代码点。例如。 CJK字符通常以UTF-8编码为3个字节,在UTF-16中编码为2个字节。

Golang中的字符串文字是UTF-8。