Go 中的 rune
是什么?
我一直在谷歌搜索,但 Golang 只在一行中说:rune
是 int32
的别名。
但是整数怎么会像交换案例一样到处使用呢?
下面是一个函数swapcase。 <=
和 -
是什么?
为什么 switch
没有任何参数?
&&
应该表示 和,但 r <= 'z'
是什么?
func SwapRune(r rune) rune {
switch {
case 'a' <= r && r <= 'z':
return r - 'a' + 'A'
case 'A' <= r && r <= 'Z':
return r - 'A' + 'a'
default:
return r
}
}
他们中的大多数来自http://play.golang.org/p/H6wjLZj6lW
func SwapCase(str string) string {
return strings.Map(SwapRune, str)
}
我知道这是将 rune
映射到 string
以便它可以返回交换的字符串。但我不明白 rune
或 byte
究竟是如何工作的。
[]rune
可以设置为布尔、数字或字符串类型。请参阅stackoverflow.com/a/62739051/12817546。
符文文字只是 32 位整数值(但它们是无类型常量,因此它们的类型可以改变)。它们代表 unicode 代码点。例如,符文文字 'a'
实际上是数字 97
。
因此,您的程序几乎等同于:
package main
import "fmt"
func SwapRune(r rune) rune {
switch {
case 97 <= r && r <= 122:
return r - 32
case 65 <= r && r <= 90:
return r + 32
default:
return r
}
}
func main() {
fmt.Println(SwapRune('a'))
}
如果您要查看 Unicode 映射,这应该很明显,它与该范围内的 ASCII 相同。此外,32 实际上是字符的大写和小写代码点之间的偏移量。因此,通过将 32
添加到 'A'
,您得到 'a'
,反之亦然。
来自 Go 语言发行说明:http://golang.org/doc/go1#rune
符文是一种类型。它占用 32 位,用于表示 Unicode CodePoint。作为类比,以“ASCII”编码的英文字符集有 128 个代码点。因此能够容纳在一个字节(8位)内。根据这个(错误的)假设,C 将字符视为“字节”char
,将“字符串”视为“字符序列”char*
。
但猜猜怎么了。除了“abcde..”符号之外,人类还发明了许多其他符号。而且有这么多,我们需要 32 位来编码它们。
在 golang 中,string
是 bytes
的序列。但是,由于多个字节可以表示一个符文代码点,因此字符串值也可以包含符文。因此,它可以转换为 []rune
,反之亦然。
unicode 包 http://golang.org/pkg/unicode/ 可以让您体验到挑战的丰富性。
rune
类似于 int32
并且有很多位。
not bytes
。然后,您可能会说:“字符串由符文组成,符文由字节组成”之类的。然后再一次。这并不完全正确。
我试图让我的语言保持简单,以便外行能够理解rune
。
符文是一个字符。而已。
它是单个字符。它是来自世界任何地方的任何语言的任何字母表中的字符。
要获取我们使用的字符串
double-quotes ""
或者
back-ticks ``
字符串与符文不同。在符文中,我们使用
single-quotes ''
现在,rune 也是 int32
...呃什么的别名?
https://i.stack.imgur.com/JfUIZ.png
每个字符都映射到某个数字,因此它就是我们要存储的数字。例如,a 映射到 97,当我们存储该数字时,它只是数字,因此 rune 是 int32 的别名。但不仅仅是任何数字。它是一个包含 32 个“零和一”或“4”字节的数字。 (注:UTF-8 是一种 4 字节的编码方案)
符文与弦有什么关系?
字符串是符文的集合。在以下代码中:
package main
import (
"fmt"
)
func main() {
fmt.Println([]byte("Hello"))
}
我们尝试将字符串转换为字节流。输出是:
[72 101 108 108 111]
我们可以看到组成该字符串的每个字节都是一个符文。
A string is not a collection of runes
严格来说,这是不正确的。相反,字符串是一个字节切片,用 utf8 编码。 string 中的每个 char 实际上占用 1 ~ 3 个字节,而每个 rune 占用 4 个字节。您可以在字符串和 []rune 之间进行转换,但它们是不同的。
我没有足够的声望对 fabrizioM 的 answer 发表评论,所以我将不得不在此处发表评论。
Fabrizio 的回答在很大程度上是正确的,他当然抓住了问题的本质——尽管必须做出区分。
字符串不一定是符文序列。它是“字节切片”的包装器,切片是 Go 数组的包装器。这有什么区别?
符文类型必须是 32 位值,这意味着符文类型值的序列必然具有一定数量的 x*32 位。作为字节序列的字符串,其长度为 x*8 位。如果所有字符串实际上都是 Unicode,那么这种差异不会产生任何影响。然而,由于字符串是字节切片,Go 可以使用 ASCII 或任何其他任意字节编码。
但是,字符串文字需要写入以 UTF-8 编码的源代码中。
信息来源:http://blog.golang.org/strings
(感觉上面的答案仍然没有很清楚地说明 string
和 []rune
之间的差异和关系,所以我会尝试添加另一个答案并举例说明。)
正如 @Strangework
的回答所说,string
和 []rune
完全不同。
差异 - string
& []rune
:
字符串值是一个只读字节切片。并且,字符串文字以 utf-8 编码。 string 中的每个 char 实际上占用 1 ~ 3 个字节,而每个 rune 占用 4 个字节
对于字符串,len() 和 index 都是基于字节的。
对于 []rune,len() 和 index 都基于 rune(或 int32)。
关系 - string
& []rune
:
当您从字符串转换为 []rune 时,该字符串中的每个 utf-8 字符都会变成一个 rune。
同样,在反向转换中,当从 []rune 转换为 string 时,每个 rune 都成为字符串中的一个 utf-8 char。
提示:
您可以在字符串和 []rune 之间进行转换,但它们在类型和整体大小上仍然不同。
(我会添加一个例子来更清楚地表明这一点。)
代码
string_rune_compare.go:
// string & rune compare,
package main
import "fmt"
// string & rune compare,
func stringAndRuneCompare() {
// string,
s := "hello你好"
fmt.Printf("%s, type: %T, len: %d\n", s, s, len(s))
fmt.Printf("s[%d]: %v, type: %T\n", 0, s[0], s[0])
li := len(s) - 1 // last index,
fmt.Printf("s[%d]: %v, type: %T\n\n", li, s[li], s[li])
// []rune
rs := []rune(s)
fmt.Printf("%v, type: %T, len: %d\n", rs, rs, len(rs))
}
func main() {
stringAndRuneCompare()
}
执行:
去运行 string_rune_compare.go
输出:
hello你好, type: string, len: 11
s[0]: 104, type: uint8
s[10]: 189, type: uint8
[104 101 108 108 111 20320 22909], type: []int32, len: 7
解释:
字符串 hello你好 长度为 11,因为前 5 个字符每个只占 1 个字节,而后 2 个中文字符每个占 3 个字节。因此,总字节数 = 5 * 1 + 2 * 3 = 11 由于字符串上的 len() 基于字节,因此第一行打印 len: 11 由于字符串上的索引也基于字节,因此以下 2 行打印值uint8 类型(因为 byte 是 uint8 的别名类型,在 go 中)。
因此,总字节数 = 5 * 1 + 2 * 3 = 11
由于字符串上的 len() 是基于字节的,因此第一行打印 len: 11
由于字符串上的索引也是基于字节的,因此以下 2 行打印 uint8 类型的值(因为 byte 是 uint8 的别名类型,在 go 中)。
将字符串转换为 []rune 时,发现 7 个 utf8 字符,因此有 7 个 rune。由于 []rune 上的 len() 是基于 rune 的,因此最后一行打印 len: 7. 如果通过索引操作 []rune,它将访问基于 rune。由于每个 rune 都来自原始字符串中的 utf8 字符,因此您也可以说 []rune 上的 len() 和索引操作都基于 utf8 字符。
由于 []rune 上的 len() 是基于 rune 的,因此最后一行打印了 len: 7。
如果你通过索引操作[]rune,它会根据rune来访问。由于每个 rune 都来自原始字符串中的 utf8 字符,因此您也可以说 []rune 上的 len() 和索引操作都基于 utf8 字符。
fmt.Println("hello你好"[0])
时,它返回实际的 UTF-8 代码点而不是字节。
s[0]
,它打印s[0]: 104, type: uint8
,类型为uint8
,表示它是一个字节。对于像 h
utf-8 这样的 ASCII 字符也是用单字节来表示,所以码位和单字节一样;但是对于像 你
这样的中文字符,它使用 3 个字节。
符文相关的部分大家都讲过了,我就不多说了。
但是,还有一个与 switch
没有任何参数有关的问题。这仅仅是因为在 Golang 中,不带表达式的 switch
是表达 if/else 逻辑的另一种方式。例如,这样写:
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
和写这个一样:
t := time.Now()
if t.Hour() < 12 {
fmt.Println("It's before noon")
} else {
fmt.Println("It's after noon")
}
您可以阅读更多here。
rune 是一个 int32 值,因此它是一种 Go 类型,用于表示 Unicode 代码点。 Unicode 代码点或代码位置是通常用于表示单个 Unicode 字符的数值;
程序
package main
import (
"fmt"
)
func main() {
words := "€25 or less"
fmt.Println("as string slice")
fmt.Println(words, len(words))
runes := []rune(words)
fmt.Println("\nas []rune slice")
fmt.Printf("%v, len:%d\n", runes, len(runes))
bytes := []byte(words)
fmt.Println("\nas []byte slice")
fmt.Printf("%v, len:%d\n", bytes, len(bytes))
}
输出
as string slice
€25 or less 13
as []rune slice
[8364 50 53 32 111 114 32 108 101 115 115], len:11
as []byte slice
[226 130 172 50 53 32 111 114 32 108 101 115 115], len:13
如您所见,欧元符号“€”由 3 个字节表示 - 226、130 和 172。符文表示一个字符 - 任何字符,无论是象形文字。一个符文的 32 位足以代表当今世界上的所有字符。因此,欧元符号“€”的符文表示是 8364。
对于 128 个 ASCII 字符,一个字节(8 位)就足够了。因此,数字或字母的符文和字节表示是相同的。例如:2 用 50 表示。
字符串的字节表示在长度上始终大于或等于其符文表示,因为某些字符由超过一个字节但在 32 位之内表示,这就是符文。
https://play.golang.org/p/y93woDLs4Qe
rune 是 int32 的别名,在所有方面都等效于 int32。它用于区分字符值和整数值。
l = 108,o = 111
Rune 是 int32 类型的别名。它表示单个 Unicode 代码点。 Unicode 联盟将数字值(称为代码点)分配给超过一百万个唯一字符。例如,65 是字母 A、66 -> B 的代码点(来源:Get Programming with Go)
unicode.ToLower(r rune) rune
。func SwapRune(r rune) rune { if unicode.IsUpper(r) { r = unicode.ToLower(r) } else { r = unicode.ToUpper(r) }; return r }