字符串

基本使用

UTF-8ASCII

声明和初始化

字符串的声明和初始化非常简单,举例如下:

var str string         // 声明字符串变量
str = "Hello World"    // 变量初始化
str2 := "你好呀"   // 也可以同时进行声明和初始化

格式化输出

len()fmtPrintf
fmt.Printf("The length of \"%s\" is %d \n", str, len(str)) 
fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)

转义字符

Go 语言的字符串不支持单引号,只能通过双引号定义字符串字面值,如果要对特定字符进行转义,可以通过 \ 实现,就像我们上面在字符串中转义双引号和换行符那样,常见的需要转义的字符如下所示:

\n\r\t\u\\

所以,上述打印代码输出结果为:

The length of "Hello world" is 11 
The first character of "Hello world" is H.

除此之外,你可以通过如下方式在字符串中包含 ":

label := `Search results for "Golang":`

多行字符串

对于多行字符串,也可以通过 ` 构建:

results := `Search results for "Golang":
- Go
- Golang
Golang Programming
`
fmt.Printf("%s", results)

打印结果如下:

Search results for "Golang":
- Go
- Golang
- Golang Programming

当然,使用 + 连接符也是可以的:

results := "Search results for \"Golang\":\n" +
"- Go\n" +
"- Golang\n" +
"- Golang Programming\n"
fmt.Printf("%s", results)

打印结果是一样的,但是要多输入不少字符,也不如上一种实现优雅。

不可变值类型

虽然可以通过数组下标方式访问字符串中的字符:

ch := str[0] // 取字符串的第一个字符

但是和数组不同,在 Go 语言中,字符串是一种不可变值类型,一旦初始化之后,它的内容不能被修改,比如看下面这个例子:

str := "Hello world"
str[0] = 'X' // 编译错误

编译器会报类似如下的错误:

cannot assign to str[0]

字符编码

UTF-8UnicodeANSI
ANSIUTF-8WindowsGBKUTF-8
UTF-8Unicodeiconv

字符串操作

字符串连接

Go 内置提供了丰富的字符串函数,常见的操作包含连接、获取长度和指定字符,获取长度和指定字符前面已经介绍过,字符串连接只需要通过 + 连接符即可:

str = str + ", 世界"
str += ", 世界"  // 上述语句也可以简写为这样,效果完全一样
+
str = str +
        ", 世界"

字符串切片

在 Go 语言中,可以通过字符串切片实现获取子串的功能:

str := "hello, world"
str1 := str[:5]  // 获取索引5(不含)之前的子串
str2 := str[7:]  // 获取索引7(含)之后的子串
str3 := str[0:5]  // 获取从索引0(含)到索引5(不含)之间的子串
fmt.Println("str1:", str1)
fmt.Println("str2:", str2)
fmt.Println("str3:", str3)
str[0:5][0,5)str[:5][0,5)str[7:][7:len(str)]

所以,上述代码打印结果如下:

str1: hello
str2: world
str3: hello
str[:]
strings 包

字符串遍历

Go 语言支持两种方式遍历字符串。

一种是以字节数组的方式遍历:

str := "Hello, 世界" 
n := len(str) 
for i := 0; i < n; i++ {
    ch := str[i]    // 依据下标取字符串中的字符,ch 类型为 byte
    fmt.Println(i, ch) 
}

这个例子的输出结果为:

0 72 
1 101 
2 108 
3 108 
4 111 
5 44 
6 32 
7 228 
8 184 
9 150 
10 231 
11 149 
12 140

可以看出,这个字符串长度为 13,尽管从直观上来说,这个字符串应该只有 9 个字符。这是因为每个中文字符在 UTF-8 中占 3 个字节,而不是 1 个字节。

另一种是以 Unicode 字符遍历:

str := "Hello, 世界" 
for i, ch := range str { 
    fmt.Println(i, ch)    // ch 的类型为 rune 
}

输出结果为:

0 72 
1 101 
2 108 
3 108 
4 111 
5 44 
6 32 
7 19990 
10 30028
Unicoderunebyte

看到这里可能你有点懵,会好奇 Go 底层到底是如何存储字符串的,为什么不同遍历方式获取的结果不同呢?下面就来给大家简单掰扯掰扯。

底层字符类型

Go 语言对字符串中的单个字符进行了单独的类型支持,在 Go 语言中支持两种字符类型:

byteUTF-8uint8runeUnicodeuint32runeunicode
runebytestringstring
runebytestring

字节和字符

刚刚上面标注了字节和字符,现在我们来梳理字符字节的概念

存储单位 字节

  • 计算机存储信息的最小单位,称之为位 bit,二进制的一个0或1叫一位
  • 计算机存储容量基本单位是字节 Byte,8个二进制位组成 1 个字节

信息表示单位 字符

  • 字符是一种符号,像 英文a和中文阿 就是不同字符
  • 不同的字符在不同的编码格式下,所需要的存储单位不一样
  • ASCLII 编码中一个英文字母一字节,一个汉字两字节
  • UTF-8 编码中 一个英文字母一字节,一个常见汉字3字节,不常用的超大字符集汉字4字节

UTF-8 和 Unicode 的区别

UTF-8Unicode
UnicodeASCIIISO 8859-1UnicodeUTF-8UTF-16UTF8MB4GBK
UTF-8UnicodeUnicodeUTF-8UTF-8Unicodeunicode/utf8UTF-8Unicode
UnicodeUTF-8
lenUTF-8rangeUnicode
APIUTF-8
UnicodeUnicodeUTF-8

从编码上来分析

bytebyteruneUnicoderune

通俗一点

byterune

将 Unicode 编码转化为可打印字符

string
str := "Hello, 世界" 
for i, ch := range str { 
    fmt.Println(i, string(ch))
}

对应的打印结果如下:

0 H
1 e
2 l
3 l
4 o
5 ,
6  
7 世
10 界
UTF-8