Golang Go语言中 string 和 rune
字符集和编码方式
字符集(character set)是一组字符(character)的集合. 简单的如 ASCII 字符集定义了英语中常见的 128 个字符. 复杂的如 Unicode 字符集定义了超过 14 万个字符, 囊括了绝大多数语言文字的需求. Unicode 为每一个字符分配一个唯一的 code point, 从 0 到 0x10FFFF, 通常表示为 U+X, X 为 4 到 6 个十六进制数字, 如英语字符 A 对应 U+0041.
编码(encoding)是指如何将字符集中的字符转化为计算机可以理解的二进制形式. UTF-8 和 UTF-16 是最常见的两种编码方式, 针对的都是 Unicode 字符集.
UTF-8 用一到四个字节来表示一个 Unicode 字符 code point. 在 UTF-8 中, 127 个 ASCII 字符仅需一个字节来存储, 使得其在英语环境中非常高效, 同时页完全兼容了 ASCII 编码.
UTF-16 用二或四个字节来代表一个 Unicode code point. 其在包含中文, 日文或韩文等的场景中, 可能比 UTF-8 更高效.
rune
Go 中的 rune 是指 int32, 对应 Unicode 字符集中的 code point.
其值包括在单引号中,既可以是字符, 如 'a'
, 也可以是由反斜杠转义的内容, 如
- 反斜杠加单个字符代表一些特殊值, 包括
\n
,\t
,\'
,\\
等 - \x 跟两个十六进制数字, 如
\x41
代表 A - \u 跟四个十六进制数字, 如
\u0041
代表 A - \U 跟八个十六进制数字, 如
\U00000041
代表 A - \ 跟三个 8 进制数字, 如
\x101
代表 a
func ExampleRune() {
r := 'A'
fmt.Println(r == '\x41')
fmt.Println(r == '\u0041')
fmt.Println(r == '\U00000041')
fmt.Println(r == '\101')
// Output: true
// true
// true
// true
}
string
Go 中的 string 有两种形式: raw 和 interpreted. 前者用反引号`, 如 `foo`, 后者用双引号, 如 "foo".
我们可以在 interpreted literal string 中使用 rune 的反斜杠转义来表示任意字符, 如:
func ExampleLiteralString() {
fmt.Print("Hello\n")
fmt.Print("H\x65llo\n")
fmt.Print("H\u0065llo\n")
fmt.Print("H\145llo\n")
// Output: Hello
// Hello
// Hello
// Hello
}
我们并不能在在 interpreted literal string 中转义反引号, "`" 不是一个合法的字符串, 因为反引号在 Go 中用于表示 raw literal string. 不同于 interpreted string, raw string 并不处理任何转义.
func ExampleRawString() {
fmt.Println(`H\x64llo`)
// Output: H\x64llo
}
Go 中的 string 实质是只读的字节数组(read-only slice of bytes). string literal 按 UTF-8 编码后存放在字节数组中, 如 "中国" 就需要占用 6 个字节.
func ExampleStringIsBytes() {
fmt.Println(len("中国"))
fmt.Println(string([]byte{0xe4, 0xb8, 0xad, 0xe5, 0x9b, 0xbd}))
// Output: 6
// 中国
}
这种情况下, 遍历字符串是逐字节遍历字节数组而不是逐字符遍历字符串. 大多数人对此感知不明显的主要原因是:
- 大部分时候处理的都是 ASCII 字符, 仅占一字节
- 诸如 for-range 等语法糖是按字符遍历
func ExampleIterString() {
s := "中国"
for i := 0; i < len(s); i++ {
fmt.Printf("%x,", s[i])
}
fmt.Println("")
for _, c := range s {
fmt.Printf("%c,", c)
}
fmt.Println("")
// Output: e4,b8,ad,e5,9b,bd,
// 中,国,
}
Reference
Golang Go语言中 string 和 rune
更多关于Golang Go语言中 string 和 rune的实战教程也可以访问 https://www.itying.com/category-94-b0.html
为什么 go 的 unicode 要用 i32 而不是 u32 ,完全没有说得通的理由吧,太奇怪了
更多关于Golang Go语言中 string 和 rune的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这个问题 Google 一搜一大堆,大致有几点:1. Unicode 编码是从 U+0000 到 U+10FFFF ,即使是有符号的 int32 也可以表示。
https://chat.openai.com/share/158c2561-93d5-4d62-9cfe-b784944c40ef
提供个其他思路,LLM 回答这类问题也不错。
我个人看法是为什么要用 uint32 ?
在Go语言中,string
和 rune
是处理文本数据时常用的两种类型,它们各自有不同的用途和特点。
string
类型是Go语言中的基本数据类型之一,用于表示不可变的字节序列。在Go中,字符串是以UTF-8编码存储的,这意味着字符串可以包含任意Unicode字符。由于字符串是不可变的,你不能直接修改字符串中的某个字符,但可以通过创建新的字符串来实现修改。
rune
类型则是Go语言中的别名,它等价于 int32
类型,通常用于表示单个Unicode码点。在处理需要精确到字符级别的文本时,rune
非常有用。例如,当你需要遍历字符串中的每个字符,并且这些字符可能是多字节的UTF-8编码时,使用 rune
可以确保你能够正确地处理每个字符。
在处理字符串时,如果你需要获取字符串中某个位置的字符,并且这个字符可能是多字节的,那么最好使用 rune
类型和 range
关键字来遍历字符串。这样可以确保你能够正确地处理每个字符,而不会因为字符编码的问题而出现错误。
总的来说,string
类型适用于表示和操作整个字符串,而 rune
类型则适用于需要精确到字符级别的文本处理。在实际编程中,你可以根据具体需求选择使用哪种类型,以便更好地完成任务。