我正在阅读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个。
为什么范围内的位置与每个值应消耗的内存总量不同?
答案 0 :(得分:4)
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。
如果您想了解有关该主题的更多信息,请阅读必读博客文章:
答案 1 :(得分:0)
rune
是代码点。代码点只是整数。如果您愿意,甚至可以使用int64
来存储它。 (但Unicode只有1,114,112个代码点,所以int32
应该是正确的选择。难怪rune
是Golang中int32的别名。)
不同的编码方案以不同的方式编码代码点。例如。 CJK字符通常以UTF-8编码为3个字节,在UTF-16中编码为2个字节。
Golang中的字符串文字是UTF-8。