4. switch-case
语句
Go语言中的switch-case
语句要比C语言的switch-case
语句更加通用,表达式的值不必为常量,甚至不必为证书。case
按照从上往下的顺序进行求值,直到找到匹配的项。可以将多个if-else
语句改写成一个switch-case
语句。Go语言中的switch-case
语句使用比较灵活,语法设计以使用方便为主。
Go语言改进了传统的switch-case
语句的语法设计:case
与case
之间是独立的代码块,不需要通过break
语句跳出当前case
代码块,以避免执行到下一行。示例代码如下。
var a = "love"
switch a {
case "love":
fmt.Println("love")
case "programming":
fmt.Println("programming")
default:
fmt.Println("none")
}
以上代码的运行结果如下:
love
在上面例子中,每一个case
都是字符串格式,且使用了default
分支。Go语言规定每个switch
只能有一个default
分支。
同时,Go语言还支持一些新的写法,比如一个分支多个值、分支表达式。
(1)一个分支多个值。
当需要将多个case
放在一起时,可以写成下面这样:
var language = "golang"
switch language{
case "golang", "java":
fmt.Println("popular languages")
}
以上代码的运行结果如下:
popular languages
在一个分支多个值的case
表达式中,使用逗号分隔值。
(2)分支表达式
case
语句即可以是常量,也可以和if
一样添加表达式。示例如下:
var r int = 6
switch {
case r > 1 && r < 10:
fmt.Println(r)
}
在这种情况下,switch
后面不再需要添加用于判断的变量。
5. goto
语句
在Go语言中,可以通过 goto
语句跳转到标签,进行代码间的无条件跳转。另外,goto
语句在快速跳出循环、避免重复退出方面也有一定的帮助。使用goto
语句能简化一些代码的实现过程。
在满足条件时,如果需要连续退出两层循环,则传统的编码方式如下:
func main(){
var isBreak bool
for x := 0; x < 20; x++{
for y := 0; y < 20; y++{
if y == 2{
isBreak = true
break
}
}
if isBreak{
break
}
}
fmt.Println("over")
}
将上面的代码使用Go语言的goto
语句进行优化:
func main(){
for x := 0; x < 20; x++{
for y := 0; y < 20; y++{
if y == 2{
goto breakTag
}
}
}
return
breakTag:
fmt.Println("done")
}
在以上代码中,使用goto
语句goto breakTag
来跳转到指明的标签处。breakTag
是自定义的标签。
在日常开发中,经常会遇到“多错误处理”问题,在“多错误处理”中往往存在代码重复的问题。例如:
func main(){
// 省略前面代码
err := getUserInfo()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
err := getEmail()
if err != nil {
fmt.Println(err)
exitProcess()
return
}
fmt.Println("over")
}
在上面代码中,有一部分是重复的代码。如果后期需要在这些代码中添加更多的判断条件,则需要在这些雷同的代码中重复修改,极易造成疏忽和错误。这时可以通过使用goto
语句来处理:
func main(){
// 省略前面代码
err := getUserInfo()
if err != nil {
goto doExit
}
err := getEmail()
if err != nil {
goto doExit
}
fmt.Println("over")
return
doExit:
fmt.Println(err)
exitProcess()
}
6. break
语句
Go语言中的break
语句可以结束for
、switch
和select
的代码块。另外,还可以在break
语句后面添加标签,表示退出标签对应的代码块。添加的标签必须定义在对应的for
、switch
和select
的代码块上。
通过指定标签跳出循环的示例如下。
代码 chapter1/1.3-break.go 通过指定标签跳出循环
package main
import "fmt"
func main(){
OuterLoop:
for i := 0; i < 2; i++{
for j:=0; j < 5; j++{
switch j { // 用switch 进行数值分支判断
case 1:
fmt.Println(i, j)
break OuterLoop
case 2:
fmt.Println(i, j)
break OuterLoop
}
}
}
}
以上代码的运行结果如下:
0 1
7. continue
语句
在Go语言中,continue
语句用于结束当前循环,并开始下一次的循环迭代过程。它仅限在for
循环内使用。在continue
语句后添加标签,表示结束标签对应语句的当前循环,并开启下一次的外层循环。continue
语句的使用示例如下。
代码 chapter1/1.3-continue.go continue语句的使用示例
package main
import "fmt"
func main(){
OuterLoop:
for i:=0; i<2; i++{
for j:=0; j<5; j++{
switch j {
case 3:
fmt.Println(i, j)
continue OuterLoop
}
}
}
}
以上代码的运行结果如下:
0 3
1 3
1.4 Go数据类型
Go语言的基本数据类型分为布尔型、数学类型、字符串类型、复合类型这4种。其中复合类型又分为:数组类型、切片类型、Map
类型、结构体类型。Go语言常见基本数据类型如下:
类型 | 说明 |
---|---|
布尔型 | 布尔型的值只可以是常量true 或者false 。一个简单的例子:var b bool = true |
数字类型 | 包含uint8 uint16 unint32 uint64 int8 int16 int32 int64 float32 float64 complex64 complex128 byte rune unit int uintptr(无符号整型,用于存放一个指针) |
字符串类型 | 字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8 编码标识Unicode 文本 |
复合类型 | 包含数组类型、切片类型、Map 类型、结构体类型 |
1.4.1 布尔型
布尔型的值只可以是常量true
或者false
。if
和for
语句的条件部分都是布尔类型的值,并且==
和<
等比较操作也会产生布尔型的值。
一元操作赴(!
)对应逻辑“非”操作,因此!true
的值为false
。更复杂的写法是(!true==false)==true
。在实际开发种,应尽量采用比较简洁的布尔表达式。
var aVar = 100
fmt.Println(aVar == 50)
fmt.Println(aVar == 100)
fmt.Println(aVar != 50)
fmt.Println(aVar != 100)
布尔值可以和&&(AND)
和||(OR)
操作符结合。如果运算符左边的值已经可以确定整个布尔值表达式的值,则运算符右边的值将不再被求值,因此下面的表达式是安全的:
str1 == "java" && str2 == "golang"
布尔值并不会隐式转化为数字值0或1,反之亦然,必须使用if
语句显式地进行转换:
i := 0
b := true
if b {
i = 1
}
如果需要经常做类似的转换,则可以将转换的代码封装成一个函数,如下所示:
// 如果b为真,则boolToInt()函数返回1;如果为假,则boolToInt()函数返回0
// 将布尔型转换为整型
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
1.4.2 数字类型
Go语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。
Go语言也有基于架构的类型,例如:int
、uint
和uintptr
。这些类型的长度都是根据运行程序所在操作系统类型所决定。
1.4.3 字符串类型
字符串是由一串固定长度的字符连接起来的字符序列。Go语言种的字符串是由单个字节连接起来的。Go语言种的字符串的字节使用UTF-8
编码来便是Unicode
文本。UTF-8
是一种被广泛使用的编码格式,是文本文件的标准编码。包括XML
和JSON
在内都使用该编码。
由于该编码占用字节长度的不定性,所以在Go语言种,字符串也可能根据需要占用1~4byte
。
字符串是一种值类型。更深入地讲,字符串是字节的定长数组。
1. 字符串的声明和初始化
声明和初始化字符串非常容易:
str := "hello string!"
上面的代码声明了字符串变量str
,其内容为“hello string!
”。
2. 字符串的转义
在Go语言中,字符串字面量使用英文双引号("
)或者反引号(`)来创建。
- 双引号用来创建可解析的字符串,支持转义,但不能用来引用多行;
- 反引号用来创建原生的字符串字面量,可能由多行组成,但不支持转义,并且可以包含除反引号外的其他所有字符。
用双引号来创建可解析的字符串应用很广泛,用反引号来创建原生的字符串则多用于书写多行消息、HTML
及正则表达式。
使用示例如下:
str1 := "\"Go Web\", I love you \n" // 支持转义,但不能用来引用多行
str2 := `Go Web,
I love you \n` // 支持多行组成,但不支持转义
println(str1)
println(str2)
以上代码的运行结果如下:
"Go Web", I love you
"Go Web",
I love you
3. 字符串的连接
虽然Go语言中的字符串是不可变的,但是字符串支持级联操作(+
)和追加操作(+=
),比如下面这个例子:
str := "I love" + " Go Web"
str += " programming"
fmt.Println(str) // I love Go Web programming
4. 字符串的操作符
字符串的内容(纯字节)可以通过标准索引法来获取:在方括号[]
内加入索引,索引从0开始计数。
假设我们定义了一个字符串str := "programming"
,则可以通过str[0]
来获取字符串str
的第1个字节,通过str[i-1]
来获取第i
个字节,通过str[len(str)-1]
来获取最后一个字节。
通过下面具体的示例,可以理解字符串的常用方法:
str := "programming"
fmt.Println(str[1]) // 获取字符串索引位置为1的原始字节,比如r为114
fmt.Println(str[1:3]) // 截取字符串索引位置为1和2的字符串(不包含最后一个)
fmt.Println(str[1:]) // 截取字符串索引位置为1到len(s)-1的字符串
fmt.Println(str[:3]) // 截取字符串索引位置为0到2的字符串(不包含3)
fmt.Println(len(str)) // 获取字符串的字节数
fmt.Println(utf8.RuneCountInString(str)) // 获取字符串字符的个数
fmt.Println([]rune(str)) // 将字符串的每一个字节转换为码点数
fmt.Println(string(str[1])) // 获取字符串索引位置为1的字符值
以上代码的运行结果如下:
114
ro
rogramming
pro
11
11
[112 114 111 103 114 97 109 109 105 110 103]
r
5. 字符串的比较
Go语言中的字符串支持常规的比较操作(<
、>
、==
、!=
、<=
、>=
),这些操作符会在内存中一个字节一个字节地进行比较,因此比较的结果是字符串自然编码的顺序。
6. 字符串的遍历
通常情况下,可以通过索引提取其字符,比如:
str := "go web"
fmt.Println(string(str[0])) // 获取索引为0的字符
以上的字符串是单字节的,通过索引可以直接提取字符。但是对于任意字符串来讲,上面并不一定可靠,因为有些字符可能有多个字节。这时就需要使用字符串切片,这样返回的将是一个字符,而不是一个字节:
str := " i love go web"
chars := []rune(str) // 把字符串转为rune切片
for _,char := range chars{
fmt.Println(string(char))
}
在Go语言中,可以用rune
或者int32
来表示一个字符。
字符可以通过+=
操作符在一个循环中往字符串末尾追加字符。但这并不是最有效的方式,还可以使用类似Java
中的StringBuilder
来实现。
var buffer bytes.Buffer // 创建一个空的bytes.Buffer
for {
if piece,ok := getNextString();ok{
// 通过WriteString()方法,将需要串联的字符串写入buffer中
buffer.WriteString(piece)
} else{
break
}
}
fmt.Println(buffer.String()) // 用于取回整个级联的字符串
如果要将字符串一个字符一个字符地迭代出来,则可以通过for-range
循环:
str := "love go web"
for index, char:= range str{
fmt.Printf("%d %U %c \n", index, char, char)
}
7. 字符串的修改
在Go语言中,不能直接修改字符串的内容,即不能通过str[i]
这种方式修改字符串中的字符。如果要修改字符串的内容,则需要先将字符串的内容复制到一个可写的变量中(一般是[]byte
或[]rune
类型的变量),然后再进行修改。在转换类型的过程中会自动复制数据。
(1)修改字节(用[]byte
)。
对于单字节字符,可以通过这种方式进行修改:
str := "Hi 世界!"
by := []byte(str) // 转换为[]byte, 数据被自动复制
by[2] = ','
fmt.Printf("%s\n", str)
fmt.Printf("%s\n", by)
(2)修改字符(用[]rune
)。
str := "Hi 世界!"
by := []rune(str) // 转换为[]rune, 数据被自动复制
by[3] = '中'
by[4] = '国'
fmt.Println(str)
fmt.Println(string(by))