Go学习——《Go语言程序设计》Chap3

字符串

本章讲解 Go 语言的字符串类型

一个 Go 语言字符串是一个任意字节的常量序列。大部分情况下,一个字符串的字节使用 UTF-8 编码表示 Unicode 文本。

字面量、操作符和转义

字符串字面量使用双引号(")或者反引号(')来创建,主要区别如下

  • "

    双引号创建可解析的字符串字面量,比如带转义,但不能用来引用多行。

  • '

    反引号创建原生的子字符串字面量,这些字符串可能由多行组成,不支持任何转义序列,并且可以包含除了反引号之外的任何字符。

可解析的字符串使用得最广泛,而原生的字符串字面量则用于书写多行消息、HTML 以及正则表达式,这里有一些例子。

如果想创建一个长的可解析字符串字面量,但又不想在代码中写同样长的一行(想要换为多行),我们可以考虑使用+级联,或者+=追加。(当然这不是来追加的更好的方式,见下一节)

1
2
3
4
book := "The Spirit Level" +
" by Richard Wilkinson"
book += " and Kate Pickett"
fmt.Print(book)

字符和字符串

一个字符串可以使用语法chars := []rune(s)转换成一个rune(即码点)切片,其中s是一个字符串类型的值。变量chars的类型为[]int32,因为runeint32的同义词(基本数据类型处解释过)。这在我们需要逐个字符解析字符串同时需要在解析过程中能产看前一个或后一个字符时会有用。相反的转换也同样简单,其语法为s := string(chars)。两个转化时间代价都是\(O(n)\)

虽然方便,但是使用+=操作符并不是一个循环中往字符串末尾追加字符串最有效的方式。一个更好的方式(Python 程序员熟悉)是准备好一个字符串切片([]string),然后使用strings.Join()函数一次性将其中所有字符串串连起来。但在 Go 语言中还有一个更好的方法,其原理类似 Java 中的StringBuilder,例子如下:

1
2
3
4
5
6
7
8
var buffer bytes.Buffer
for {
if piece, ok := getNextValidString(); ok{
buffer.WriteString(piece)
} else {
break
}
}

将一个bytes.Buffer类型中的字符串累加起来可能比+=操作符在节省内存和操作符方面高效得多,特别当级联的字符串数量很大时。

字符串索引与切片

切片并不是万能的,对于字符全是 7 位 ASCII 编码的字符情况下切片简单又方便,因为字节索引位置是一一对应的,而这种情况下字节字符也是一一对应的。

然而当处理非 ASCII 码文本则更有挑战,因为这些字符可能用一个或者多个字节表示,如下图展示。

png
png

通常我们完全不需要切片一个字符串,只需要for...range循环将其一个字符一个字符的迭代(注意这里是按字符),但是有些情况下我们确实需要使用切片来获得一个子字符串,精确得到切片索引的方法strings.Index()strings.LastIndex()

下面是一个自己写的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 我的测试
s := "What ever you input 👿"
// for ... range 循环输出
for _, x := range s {
fmt.Printf("-%c-", x)
}
fmt.Println(s)
fmt.Print("Last index of ' ' = ")
// LastIndex 说明
// LastIndex returns the index of the last instance of substr in s, or -1 if substr is not present in s.
// LastIndex 输出的是末尾,也即不包含值,对于单个字节应为该字节后面一个索引
fmt.Println(strings.LastIndex(s, " "))
// 👿最后的index实验以及输出
evilBeginIndex := strings.LastIndex(s, " ")
evilLastIndex := strings.LastIndex(s, "")
fmt.Print("Begin index of 👿: ")
fmt.Println(evilBeginIndex)
fmt.Print("Last index of 👿: ")
fmt.Println(evilLastIndex)
fmt.Println("")
fmt.Print("Differ of the two index: ")
fmt.Println(evilLastIndex - evilBeginIndex)
fmt.Print("Print the evil: ")
fmt.Println(s[evilBeginIndex:evilLastIndex])
// 输出长度
len := len(s)
fmt.Println(len)
// 一个切片 恒等式 i \in [0,len(s)]
i := len / 2
fmt.Println(s == s[:i]+s[i:])
// 一个实际的切片例子
png
png

使用 fmt 包来格式化字符串

一些常见的方法,注意区分带F前缀为,写入到指定 writer 的(不带则是输出到os.Stdout),带f后缀的则是按字符串格式format填入参数(不带则是按格式%v以空格分割、换行结尾写),这样举一反三一下就大体知道输出函数的类别了。

格式指令 含义
%% 一个%字面量
%b 一个二进制整数值(基数为 2),或者是一个(高级的)用科学技术法表示的指数为 2 的浮点数
%c 一个 Unicode 字符的码点值(rune 类型输出)
%d 一个十进制数值(基数为 10)
%e 以科学计数法 e 表示的浮点数或者复数值
%E 以科学计数法 E 表示的浮点数或者复数值
%f 以标准计数法表示的浮点数或者复数值
%g
%G
%o 一个以八进制表示的数字(基数为 8)
%p 以十六进制(基数为 16)表示的一个值的地址,前缀为 0x,字母使用小写的 a~f 表示(用于调试)
%q 使用 Go 语法以及必要时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者单引号括起来的数字
%s 以原生的 UTF-8 字节表示的字符串或者[]byte 切片,对于一个给定的文本文件或者一个能够显示 UTF-8 编码的控制台,它会产生正确的 Unicode 输出
%t 布尔值
%T 使用 Go 语法输出值的类型
%U 使用 Unicode 表示法表示整形码点
%v 使用默认格式输出的内置或者自定义类型的值,或者是使用类型的 String()方法输出的自定义值
%x 以十六进制表示的整形值(基数为 16)、字符串或者[]byte 数组,a~f 表示
%X 以十六进制表示的整形值(基数为 16)、字符串或者[]byte 数组,A~F 表示

需要注意的是,和 C/C++的 format 输出非常相似,但是存在一些细微的差别。例如。Go 语言的%d 可以用于任何整数,无论它的大小如何和有无符号(完美~)。

例子:m3u2pls