本文为译文 strings
go里string值得被提一下,因为它的实现方式和其他语言不一样。
在go中一个字符串是字节组(a slice of bytes)
在go中sting是unicode兼容的而且全部是utf8编码
访问string中的每一个独立字符
package main
import (
"fmt"
)
func printBytes(s string) {
fmt.Printf("Bytes: ")
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
}
func printChars(s string) {
fmt.Printf("Characters: ")
for i := 0; i < len(s); i++ {
fmt.Printf("%c ", s[i])
}
}
func main() {
name := "Hello World"
fmt.Printf("String: %s\n", name)
printChars(name)
fmt.Printf("\n")
printBytes(name)
fmt.Printf("\n\n")
name = "Señor"
fmt.Printf("String: %s\n", name)
printChars(name)
fmt.Printf("\n")
printBytes(name)
}
String: Hello World
Characters: H e l l o W o r l d
Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64
String: Señor
Characters: S e à ± o r
Bytes: 53 65 c3 b1 6f 72
这里有个很严重的bug,我们试图去打印Señor的每个字符,但是结果却是,S e à ± o r。为啥错了?
字符**ñ**的**unicode**码点是**U+00F1**,其**UTF8**编码的占两个字节,分别是**c3**``**b1**。按我们现在的逻辑认为每个字符都是一个字节。所以错了。utf8编码中,一个code可以占多个字节。
所以我们该如何解决这个问题呢?这里就有了rune了。
rune
rune是go的内置类型,也是int32的别名。在go中Rune就代表了一个码点。不管这个码点占据多少个字符,他都可以被Rune代表。
让我们修改上面的程序,来用Rune实现。
package main
import (
"fmt"
)
func printBytes(s string) {
fmt.Printf("Bytes: ")
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
}
func printChars(s string) {
fmt.Printf("Characters: ")
runes := []rune(s)
for i := 0; i < len(runes); i++ {
fmt.Printf("%c ", runes[i])
}
}
func main() {
name := "Hello World"
fmt.Printf("String: %s\n", name)
printChars(name)
fmt.Printf("\n")
printBytes(name)
fmt.Printf("\n\n")
name = "Señor"
fmt.Printf("String: %s\n", name)
printChars(name)
fmt.Printf("\n")
printBytes(name)
}
String: Hello World
Characters: H e l l o W o r l d
Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64
String: Señor
Characters: S e ñ o r
Bytes: 53 65 c3 b1 6f 72
使用range循环获取单个的rune
上面的程序是一种完美的形式去迭代stirng的每一个rune。但是go提供了一种更简单的方式去做这个事情。
package main
import (
"fmt"
)
func charsAndBytePosition(s string) {
for index, rune := range s {
fmt.Printf("%c starts at byte %d\n", rune, index)
}
}
func main() {
name := "Señor"
charsAndBytePosition(name)
}
S starts at byte 0
e starts at byte 1
ñ starts at byte 2
o starts at byte 4
r starts at byte 5
从上面的输出可以看出,ñ占了两个字节。因为上面的字符o开始的是byte4而不是byte3。
从一段字节中创建一个字符串。
package main
import (
"fmt"
)
func main() {
byteSlice := []byte{0x43, 0x61, 0x66, 0xC3, 0xA9}
str := string(byteSlice)
fmt.Println(str)
}
上面的的第8行程序,是字符串Café的utf8编码。程序输出Café。
如果我们输十六进制的十进制整数。上面的程序会怎么工作呢?
package main
import (
"fmt"
)
func main() {
byteSlice := []byte{67, 97, 102, 195, 169}//decimal equivalent of {'\x43', '\x61', '\x66', '\xC3', '\xA9'}
str := string(byteSlice)
fmt.Println(str)
}
同样的也是工作正常,打印Café
string length
utf8包的RuneCountInString(s string) (n int)函数,可以被用来查找string的长度。这个函数的参数是string返回rune的数量。
正如我们之前讨论的,len(s)被用作查找string bytes的数量,不会返回string的长度。我们之前讨论过很多uicode字符的码点占据了超过一个字节。使用len去查找这些string的长度,将会返回错误的string长度。
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
word1 := "Señor"
fmt.Printf("String: %s\n", word1)
fmt.Printf("Length: %d\n", utf8.RuneCountInString(word1))
fmt.Printf("Number of bytes: %d\n", len(word1))
fmt.Printf("\n")
word2 := "Pets"
fmt.Printf("String: %s\n", word2)
fmt.Printf("Length: %d\n", utf8.RuneCountInString(word2))
fmt.Printf("Number of bytes: %d\n", len(word2))
}
String: Señor
Length: 5
Number of bytes: 6
String: Pets
Length: 4
Number of bytes: 4
string 比较
The == operator is used to compare two strings for equality. If both the strings are equal, then the result is true else it's false.
package main
import (
"fmt"
)
func compareStrings(str1 string, str2 string) {
if str1 == str2 {
fmt.Printf("%s and %s are equal\n", str1, str2)
return
}
fmt.Printf("%s and %s are not equal\n", str1, str2)
}
func main() {
string1 := "Go"
string2 := "Go"
compareStrings(string1, string2)
string3 := "hello"
string4 := "world"
compareStrings(string3, string4)
}
string 拼接
字符串拼接有多种形式。
-
+拼接
string1 := "Go" string2 := "is awesome" result := string1 + " " + string2 fmt.Println(result)
-
使用
fmt包的Sprintf函数。
Sprintf函数根据输入格式的标识符返回string的结果。
string1 := "Go"
string2 := "is awesome"
result := fmt.Sprintf("%s %s", string1, string2)
fmt.Println(result)
string的不可变性(Strings are immutable)
Strings are immutable in Go. Once a string is created it's not possible to change it.
package main
import (
"fmt"
)
func mutate(s string)string {
s[0] = 'a'//any valid unicode character within single quote is a rune
return s
}
func main() {
h := "hello"
fmt.Println(mutate(h))
}
./prog.go:8:7: cannot assign to s[0]
解决string不可变的方案就是把string转成slice of rune。
package main
import (
"fmt"
)
func mutate(s []rune) string {
s[0] = 'a'
return string(s)
}
func main() {
h := "hello"
fmt.Println(mutate([]rune(h)))
}