Go常用知识
Go常用知识
1.Go语言介绍
######################################################################################################################## # Go语言介绍 # # 硬件限制:摩尔定律已然失效 # 摩尔定律:当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。 换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。 # 我们不能像之前一样把添加更多晶体管当成是解决方案,因为在较小规模上一些量子特性开始出现(如隧道效应),并且因为在同样小的 # 空间里放置更多晶体管的代价非常昂贵,每1美元可以添加的晶体管数量开始下降。 # 制造商开始从其他方面提高处理器的性能 # 1.向处理器添加越来越多的内核,如四核和八核CPU。 # 2.发明了超线程技术。 # 3.为处理器添加了更多缓存以提高性能。 # 上述解决方案也有其自身的局限性。因为成本原因我们不能无限制的为计算机添加内核,也无法无限制的添加缓存来提高性能,因为缓存越大,取值的性能越低。 # 我们没有办法在硬件上一直取得突破,我们需要提高软件的性能或者说我们需要高性能的软件。 # Go于2009年发布,当时多核处理器已经上市。Go语言在多核并发上拥有原生的设计优势,Go语言从底层原生支持并发,无须第三方库、开发者的编程技巧和开发经验。 # 很多公司,特别是中国的互联网公司,即将或者已经完成了使用 Go 语言改造旧系统的过程。经过 Go 语言重构的系统能使用更少的硬件资源获得更高的并发和I/O吞吐表现。 # 充分挖掘硬件设备的潜力也满足当前精细化运营的市场大环境。 # Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go 语言运行时会参与调度 goroutine, # 并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用CPU性能。开启一个goroutine的消耗非常小(大约2KB的内存),你可以轻松创建数百万个goroutine。 # goroutine的特点: # 1.goroutine具有可增长的分段堆栈。这意味着它们只在需要时才会使用更多内存。 # 2.goroutine的启动时间比线程快。 # 3.goroutine原生支持利用channel安全地进行通信。 # 4.goroutine共享数据结构时无需使用互斥锁。 # 同C,C++一样,Go语言也是编译型的语言,它直接将人类可读的代码编译成了处理器可以直接运行的二进制文件,执行效率更高,性能更好。 # Go 语言在性能上更接近于 Java 语言,虽然在某些测试用例上不如经过多年优化的 Java 语言,但毕竟 Java 语言已经经历了多年的积累和优化。 # Go 语言在未来的版本中会通过不断的版本优化提高单核运行性能。 # Go语言简单易学 # 1.语法简洁 # 2.代码风格统一 # 3.开发效率高
2.开发环境准备
########################################################################################################################
# 1.从零开始搭建Go语言开发环境(新)
# 1.1.下载地址
# Go官网下载地址:https://golang.org/dl/
# Go官方镜像站(推荐):https://golang.google.cn/dl/
# 1.2.GOROOT和GOPATH
# GOROOT和GOPATH都是环境变量,其中GOROOT是我们安装go开发包的路径,而从Go 1.8版本开始,Go开发包在安装完成后会为GOPATH设置一个默认目录,
# 1.3.GOPROXY
# Go1.14版本之后,都推荐使用go mod模式来管理依赖环境了,也不再强制我们把代码必须写在GOPATH下面的src目录了,你可以在你电脑的任意位置编写go代码。
# 默认GoPROXY配置是:GOPROXY=https://proxy.golang.org,direct,由于国内访问不到https://proxy.golang.org,所以我们需要换一个PROXY,
# 这里推荐使用https://goproxy.io或https://goproxy.cn。
# 可以执行下面的命令修改GOPROXY:
go env -w GOPROXY=https://goproxy.cn,direct
# 1.4.Go开发编辑器
# Go采用的是UTF-8编码的文本文件存放源代码,理论上使用任何一款文本编辑器都可以做Go语言开发,这里推荐使用VS Code和Goland。
# VS Code是微软开源的编辑器,而Goland是jetbrains出品的付费IDE。
# 1.5.VS Code
# 1.5.1.下载与安装
# VS Code官方下载地址:https://code.visualstudio.com/Download
# 1.5.2.配置
# 1.5.2.1.安装中文简体插件
# 点击左侧菜单栏最后一项管理扩展,在搜索框中输入chinese ,选中结果列表第一项,点击install安装。
# 安装完毕后右下角会提示重启VS Code,重启之后你的VS Code就显示中文啦!
# 1.5.2.2.安装go扩展
# 现在我们要为我们的VS Code编辑器安装Go扩展插件,让它支持Go语言开发。
# 1.5.2.3.安装Go语言开发工具包
# 1.Windows平台按下Ctrl+Shift+P,Mac平台按Command+Shift+P,这个时候VS Code界面会弹出一个输入框
# 2.我们在这个输入框中输入>go:install,下面会自动搜索相关命令,我们选择Go:Install/Update Tools这个命令
# 3.选中并会回车执行该命令(或者使用鼠标点击该命令)
# 4.然后点击“确定”按钮,进行安装。
# 5.如果无法安装
# 方法1.我们可以手动从github上下载工具,(执行此步骤前提需要你的电脑上已经安装
# 第一步:现在自己的GOPATH的src目录下创建golang.org/x目录
# 第二步:在终端/cmd中cd到GOPATH/src/golang.org/x目录下
# 第三步:执行git clone https://github.com/golang/tools.git tools命令
# 第四步:执行git clone https://github.com/golang/lint.git命令
# 第五步:按下Ctrl/Command+Shift+P再次执行Go:Install/Update Tools命令,在弹出的窗口全选并点击确定,这一次的安装都会SUCCESSED了。
# 经过上面的步骤就可以安装成功了。 这个时候创建一个Go文件,就能正常使用代码提示、代码格式化等工具了。
# 方法2.下载已经编译好的可执行文件
# 如果上面的步骤执行失败了或者懒得一步一步执行,可以直接下载我已经编译好的可执行文件,拷贝到自己电脑上的 GOROOT/bin 目录下。 go-tools百度云下载链接,密码:vjx2。
# 注意:特别是Mac下需要给拷贝的这些文件赋予可执行的权限。
# 1.5.2.4.配置自动保存
# 依次点击 首选项->设置,打开设置页面就能看到自动保存相关配置,可以根据自己的喜好选择自动保存的方式
# 1.5.2.5.设置字体大小
# 依次点击 首选项->设置
# 1.5.2.6.配置代码片段快捷键
# 依次点击 首选项->用户代码片段 或者 按Ctrl/Command+Shift+P,输入>snippets,选择命令并执行
# 然后弹出如下页面
# 其中$0表示最终光标提留的位置。 举个例子,我这里创建了两个快捷方式,一个是输入pln就会在编辑器中插入fmt.Println()代码;输入plf,就会插入fmt.Printf("")代码。
# {
"println":{
"prefix": "pln",
"body":"fmt.Println($0)",
"description": "println"
},
"printf":{
"prefix": "plf",
"body": "fmt.Printf(\"$0\")",
"description": "printf"
}
}
# 1.6.第一个Go程序
# 现在我们来创建第一个Go项目——hello。在我们桌面创建一个hello目录。
# 在该目录中创建一个main.go文件:
package main // 声明 main 包,表明当前是一个可执行程序
import "fmt" // 导入内置 fmt 包
func main(){ // main函数,是程序执行的入口
fmt.Println("Hello World!") // 在终端打印 Hello World!
}
# 1.7.go build
# go build表示将源代码编译成可执行文件。
# 在hello目录下执行:
go build
# 或者在其他目录执行以下命令:
go build hello
# go编译器会去 GOPATH的src目录下查找你要编译的hello项目
# 编译得到的可执行文件会保存在执行编译命令的当前目录下,如果是windows平台会在当前目录下找到hello.exe可执行文件。
# 可在终端直接执行该hello.exe文件:
c:\desktop\hello>hello.exe
Hello World!
# 我们还可以使用-o参数来指定编译后得到的可执行文件的名字。
go build -o heiheihei.exe
# 1.8.go install
# go install表示安装的意思,它先编译源代码得到可执行文件,然后将可执行文件移动到GOPATH的bin目录下。因为我们的环境变量中配置了GOPATH下的bin目录,
# 所以我们就可以在任意地方直接执行可执行文件了。
# 1.9.跨平台编译
# 默认我们go build的可执行文件都是当前操作系统可执行的文件,如果我想在windows下编译一个linux下可执行文件,那需要怎么做呢?
# 只需要指定目标操作系统的平台和处理器架构即可:
SET CGO_ENABLED=0 // 禁用CGO
SET GOOS=linux // 目标平台是linux
SET GOARCH=amd64 // 目标处理器架构是amd64
# 使用了cgo的代码是不支持跨平台编译的
# 然后再执行go build命令,得到的就是能够在Linux平台运行的可执行文件了。
# 1.9.1.Mac 下编译 Linux 和 Windows平台 64位 可执行程序:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
# 1.9.2.Linux 下编译 Mac 和 Windows 平台64位可执行程序:
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build
# 1.9.3.Windows下编译Mac平台64位可执行程序:
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build
3.Go语言基础之变量和常量
########################################################################################################################
# 3.1.标识符与关键字
# 1.标识符
# 在编程语言中标识符就是程序员定义的具有特殊意义的词,比如变量名、常量名、函数名等等。 Go语言中标识符由字母数字和_(下划线)组成,并且只能以字母和_开头。
# 2.关键字
# 关键字是指编程语言中预先定义好的具有特殊含义的标识符。 关键字和保留字都不建议用作变量名。
# Go语言中有25个关键字
# break default func interface select
# case defer go map struct
# chan else goto package switch
# const fallthrough if range type
# continue for import return var
# Go语言中还有37个保留字
# Constants: true false iota nil
# Types: int int8 int16 int32 int64
# uint uint8 uint16 uint32 uint64 uintptr
# float32 float64 complex128 complex64
# bool byte rune string error
# Functions: make len cap new append copy close delete
# complex real imag
# panic recover
#############################################################
# 3.2.变量
# Go语言中的每一个变量都有自己的类型,并且变量必须经过声明才能开始使用。
# Go语言中的变量需要声明后才能使用,同一作用域内不支持重复声明。 并且Go语言的变量声明后必须使用。
# 变量声明以关键字var开头,变量类型放在变量的后面,行尾无需分号。
# 变量声明格式
# var 变量名 变量类型
# 标准声明
var name string
var age int
var isOk bool
# 批量声明
var (
a string
b int
c bool
d float32
)
# 变量的初始化
# Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。
# 字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil。
# var 变量名 类型 = 表达式
var name string = "Q1mi"
var age int = 18
var name, age = "Q1mi", 20
# 1.类型推导
# 有时候我们会将变量的类型省略,这个时候编译器会根据等号右边的值来推导变量的类型完成初始化。
var name = "Q1mi"
var age = 18
# 2.短变量声明
# 在函数内部,可以使用更简略的 := 方式声明并初始化变量。
package main
import (
"fmt"
)
// 全局变量m
var m = 100
func main() {
n := 10
m := 200 // 此处声明局部变量m
fmt.Println(m, n)
}
# 3.匿名变量
# 在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。 匿名变量用一个下划线_表示
func foo() (int, string) {
return 10, "Q1mi"
}
func main() {
x, _ := foo()
_, y := foo()
fmt.Println("x=", x)
fmt.Println("y=", y)
}
# 匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。 (在Lua等编程语言里,匿名变量也被叫做哑元变量。)
# 4.注意事项:
# 1.函数外的每个语句都必须以关键字开始(var、const、func等)
# 2.:=不能使用在函数外。
# 3._多用于占位,表示忽略值。
###########################################################
# 3.3.常量
# 相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。
const pi = 3.1415
const e = 2.7182
# 声明了pi和e这两个常量之后,在整个程序运行期间它们的值都不能再发生变化了。
# 多个常量也可以一起声明:
const (
pi = 3.1415
e = 2.7182
)
# const同时声明多个常量时,如果省略了值则表示和上面一行的值相同。常量n1、n2、n3的值都是100。
const (
n1 = 100
n2
n3
)
# iota
# iota是go语言的常量计数器,只能在常量的表达式中使用。
# iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。
# 使用iota能简化定义,在定义枚举时很有用。
const (
n1 = iota //0
n2 //1
n3 //2
n4 //3
)
# 几个常见的iota示例
# 1.使用_跳过某些值
const (
n1 = iota //0
n2 //1
_
n4 //3
)
# 2.iota声明中间插队
const (
n1 = iota //0
n2 = 100 //100
n3 = iota //2
n4 //3
)
const n5 = iota //0
# 3.定义数量级 (这里的<<表示左移操作,1<<10表示将1的二进制表示向左移10位,也就是由1变成了10000000000,也就是十进制的1024。
# 同理2<<2表示将2的二进制表示向左移2位,也就是由10变成了1000,也就是十进制的8。)
const (
_ = iota
KB = 1 << (10 * iota)
MB = 1 << (10 * iota)
GB = 1 << (10 * iota)
TB = 1 << (10 * iota)
PB = 1 << (10 * iota)
)
# 4.多个iota定义在一行
const (
a, b = iota + 1, iota + 2 //1,2
c, d //2,3
e, f //3,4
)
4.Go语言基础之基本数据类型
########################################################################################################################
# 基本数据类型
# Go语言中有丰富的数据类型,除了基本的整型、浮点型、布尔型、字符串外,还有数组、切片、结构体、函数、map、通道(channel)等。Go 语言的基本类型和其他语言大同小异。
###########################################################
# 4.1.整型
# 整型分为以下两个大类: 按长度分为:int8、int16、int32、int64 对应的无符号整型:uint8、uint16、uint32、uint64
# 其中,uint8就是我们熟知的byte型,int16对应C语言中的short型,int64对应C语言中的long型。
###########################################################
# 4.2.特殊整型
注意: 在使用int和 uint类型时,不能假定它是32位或64位的整型,而是考虑int和uint可能在不同平台上的差异。
# 注意事项 获取对象的长度的内建len()函数返回的长度可以根据不同平台的字节长度进行变化。实际使用中,切片或 map 的元素数量等都可以用int来表示。
# 在涉及到二进制传输、读写文件的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,不要使用int和 uint。
###########################################################
# 4.3.数字字面量语法(Number literals syntax)
# Go1.13版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字
package main
import "fmt"
func main(){
// 十进制
var a int = 10
fmt.Printf("%d \n", a) // 10
fmt.Printf("%b \n", a) // 1010 占位符%b表示二进制
// 八进制 以0开头
var b int = 077
fmt.Printf("%o \n", b) // 77
// 十六进制 以0x开头
var c int = 0xff
fmt.Printf("%x \n", c) // ff
fmt.Printf("%X \n", c) // FF
}
###########################################################
# 4.4.浮点型
# Go语言支持两种浮点型数:float32和float64。这两种浮点型数据格式遵循IEEE 754标准: float32 的浮点数的最大范围约为 3.4e38,
# 可以使用常量定义:math.MaxFloat32。 float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64。
# 打印浮点数时,可以使用fmt包配合动词%f
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%f\n", math.Pi)
fmt.Printf("%.2f\n", math.Pi)
}
###########################################################
# 4.5.复数
# complex64和complex128
# 复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。
var c1 complex64
c1 = 1 + 2i
var c2 complex128
c2 = 2 + 3i
fmt.Println(c1)
fmt.Println(c2)
###########################################################
# 4.6.布尔值
# Go语言中以bool类型进行声明布尔型数据,布尔型数据只有true(真)和false(假)两个值。
# 注意:
# 1.布尔类型变量的默认值为false。
# 2.Go 语言中不允许将整型强制转换为布尔型.
# 3.布尔型无法参与数值运算,也无法与其他类型进行转换
###########################################################
# 4.7.字符串
# Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64 等)一样。
# Go语言里的字符串的内部实现使用UTF-8编码。 字符串的值为双引号(")中的内容,可以在Go语言的源码中直接添加非ASCII码字符
s1 := "hello"
s2 := "你好"
#############################
# 4.7.1.字符串转义符
package main
import (
"fmt"
)
func main() {
fmt.Println("str := \"c:\\Code\\lesson1\\go.exe\"")
}
#############################
# 4.7.2.多行字符串
# Go语言中要定义一个多行字符串时,就必须使用反引号字符:
# 反引号间换行将被作为字符串中的换行,但是所有的转义字符均无效,文本将会原样输出。
s1 := `第一行
第二行
第三行
`
fmt.Println(s1)
#############################
# 4.7.3.字符串的常用操作
# len(str) 求长度
# +或fmt.Sprintf 拼接字符串
# strings.Split 分割
# strings.contains 判断是否包含
# strings.HasPrefix,strings.HasSuffix 前缀/后缀判断
# strings.Index(),strings.LastIndex() 子串出现的位置
# strings.Join(a[]string, sep string) join操作
#############################
# 4.7.4.修改字符串
# 要修改字符串,需要先将其转换成[]rune或[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。
func changeString() {
s1 := "big"
// 强制类型转换
byteS1 := []byte(s1)
byteS1[0] = 'p'
fmt.Println(string(byteS1))
s2 := "白萝卜"
runeS2 := []rune(s2)
runeS2[0] = '红'
fmt.Println(string(runeS2))
}
###########################################################
# 4.8.byte和rune类型
# 组成每个字符串的元素叫做“字符”,可以通过遍历或者单个获取字符串元素获得字符。 字符用单引号(’)包裹起来
var a := '中'
var b := 'x'
# Go 语言的字符有以下两种:
# 1.uint8类型,或者叫 byte 型,代表了ASCII码的一个字符。
# 2.rune类型,代表一个 UTF-8字符。
# 当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32。
# Go 使用了特殊的 rune 类型来处理 Unicode,让基于 Unicode 的文本处理更为方便,也可以使用 byte 型进行默认字符串处理,性能和扩展性都有照顾。
// 遍历字符串
func traversalString() {
s := "hello沙河"
for i := 0; i < len(s); i++ { //byte
fmt.Printf("%v(%c) ", s[i], s[i])
}
fmt.Println()
for _, r := range s { //rune
fmt.Printf("%v(%c) ", r, r)
}
fmt.Println()
}
// 输出
104(h) 101(e) 108(l) 108(l) 111(o) 230(?) 178(2) 153() 230(?) 178(2) 179(3)
104(h) 101(e) 108(l) 108(l) 111(o) 27801(沙) 27827(河)
# 因为UTF8编码下一个中文汉字由3~4个字节组成,所以我们不能简单的按照字节去遍历一个包含中文的字符串,否则就会出现上面输出中第一行的结果。
# 字符串底层是一个byte数组,所以可以和[]byte类型相互转换。字符串是不能修改的 字符串是由byte字节组成,所以字符串的长度是byte字节的长度。
# rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成。
###########################################################
# 4.9.类型转换
# Go语言中只有强制类型转换,没有隐式类型转换。该语法只能在两个类型之间支持相互转换的时候使用。
# 强制类型转换的基本语法如下
# T(表达式)
# 其中,T表示要转换的类型。表达式包括变量、复杂算子和函数返回值等.
# 比如计算直角三角形的斜边长时使用math包的Sqrt()函数,该函数接收的是float64类型的参数,而变量a和b都是int类型的,这个时候就需要将a和b强制类型转换为float64类型。
func sqrtDemo() {
var a, b = 3, 4
var c int
// math.Sqrt()接收的参数是float64类型,需要强制转换
c = int(math.Sqrt(float64(a*a + b*b)))
fmt.Println(c)
}
5.Go语言基础之运算符
######################################################################################################################## # Go语言基础之运算符 # 1.算术运算符 # 2.关系运算符 # 3.逻辑运算符 # 4.位运算符 # 5.赋值运算符
6.Go语言基础之流程控制
########################################################################################################################
# 流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”。
# Go语言中最常用的流程控制有if和for,而switch和goto主要是为了简化代码、降低重复代码而生的结构,属于扩展类的流程控制。
################################################################
# 1.if else(分支结构)
if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}
# Go语言规定与if匹配的左括号{必须与if和表达式放在同一行,{放在其他位置会触发编译错误。 同理,与else匹配的{也必须与else写在同一行,
# else也必须与上一个if或else if右边的大括号在同一行。
func ifDemo1() {
score := 65
if score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
################################################################
# 2.if条件判断特殊写法
# if条件判断还有一种特殊的写法,可以在 if 表达式之前添加一个执行语句,再根据变量值进行判断
func ifDemo2() {
if score := 65; score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}
}
################################################################
# 3.for(循环结构)
for 初始语句;条件表达式;结束语句{
循环体语句
}
# 3.1.条件表达式返回true时循环体不停地进行循环,直到条件表达式返回false时自动退出循环。
func forDemo() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
# 3.2.for循环的初始语句可以被忽略,但是初始语句后的分号必须要写
func forDemo2() {
i := 0
for ; i < 10; i++ {
fmt.Println(i)
}
}
# 3.3.or循环的初始语句和结束语句都可以省略
# 这种写法类似于其他编程语言中的while,在while后添加一个条件表达式,满足条件表达式时持续循环,否则结束循环。
func forDemo3() {
i := 0
for i < 10 {
fmt.Println(i)
i++
}
}
#
# 3.4.无限循环
for循环可以通过break、goto、return、panic语句强制退出循环。
for {
循环体语句
}
################################################################
# 4.for range(键值循环)
# Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)。 通过for range遍历的返回值有以下规律:
# 数组、切片、字符串返回索引和值。
# map返回键和值。
# 通道(channel)只返回通道内的值。
################################################################
# 5.switch case
# 使用switch语句可方便地对大量的值进行条件判断。
func switchDemo1() {
finger := 3
switch finger {
case 1:
fmt.Println("大拇指")
case 2:
fmt.Println("食指")
case 3:
fmt.Println("中指")
case 4:
fmt.Println("无名指")
case 5:
fmt.Println("小拇指")
default:
fmt.Println("无效的输入!")
}
}
# Go语言规定每个switch只能有一个default分支。
# 一个分支可以有多个值,多个case值中间使用英文逗号分隔。
func testSwitch3() {
switch n := 7; n {
case 1, 3, 5, 7, 9:
fmt.Println("奇数")
case 2, 4, 6, 8:
fmt.Println("偶数")
default:
fmt.Println(n)
}
# 分支还可以使用表达式,这时候switch语句后面不需要再跟判断变量。
func switchDemo4() {
age := 30
switch {
case age < 25:
fmt.Println("好好学习吧")
case age > 25 && age < 35:
fmt.Println("好好工作吧")
case age > 60:
fmt.Println("好好享受吧")
default:
fmt.Println("活着真好")
}
}
# fallthrough语法可以执行满足条件的case的下一个case,是为了兼容C语言中的case设计的。
func switchDemo5() {
s := "a"
switch {
case s == "a":
fmt.Println("a")
fallthrough
case s == "b":
fmt.Println("b")
case s == "c":
fmt.Println("c")
default:
fmt.Println("...")
}
}
################################################################
# 6.goto(跳转到指定标签)
# goto语句通过标签进行代码间的无条件跳转。goto语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto语句能简化一些代码的实现过程。
# 例如双层嵌套的for循环要退出时:
func gotoDemo1() {
var breakFlag bool
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
breakFlag = true
break
}
fmt.Printf("%v-%v\n", i, j)
}
// 外层for循环判断
if breakFlag {
break
}
}
}
# 使用goto语句能简化代码
func gotoDemo2() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
goto breakTag
}
fmt.Printf("%v-%v\n", i, j)
}
}
return
// 标签
breakTag:
fmt.Println("结束for循环")
}
################################################################
# 7.break(跳出循环)
# break语句可以结束for、switch和select的代码块。
# break语句还可以在语句后面添加标签,表示退出某个标签对应的代码块,标签要求必须定义在对应的for、switch和 select的代码块上。
func breakDemo1() {
BREAKDEMO1:
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
break BREAKDEMO1
}
fmt.Printf("%v-%v\n", i, j)
}
}
fmt.Println("...")
}
################################################################
# 8.continue(继续下次循环)
# continue语句可以结束当前循环,开始下一次的循环迭代过程,仅限在for循环内使用。
# 在 continue语句后添加标签时,表示开始标签对应的循环。
func continueDemo() {
forloop1:
for i := 0; i < 5; i++ {
// forloop2:
for j := 0; j < 5; j++ {
if i == 2 && j == 2 {
continue forloop1
}
fmt.Printf("%v-%v\n", i, j)
}
}
}
7.Go语言基础之数组
########################################################################################################################
# 数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。
# 基本语法:
// 定义一个长度为3元素类型为int的数组a
var a [3]int
################################################################
# 7.1.数组定义
# var 数组变量名 [元素数量]T
# 比如:var a [5]int, 数组的长度必须是常量,并且长度是数组类型的一部分。一旦定义,长度不能变。 [5]int和[10]int是不同的类型。
var a [3]int
var b [4]int
a = b //不可以这样做,因为此时a和b是不同的类型
# 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1,访问越界(下标在合法范围之外),则触发访问越界,会panic。
################################################################
# 7.2.数组的初始化
# 数组的初始化也有很多方式。
###############################
# 7.2.1.方法一
# 初始化数组时可以使用初始化列表来设置数组元素的值。
func main() {
var testArray [3]int //数组会初始化为int类型的零值
var numArray = [3]int{1, 2} //使用指定的初始值完成初始化
var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2 0]
fmt.Println(cityArray) //[北京 上海 深圳]
}
###############################
# 7.2.2.方法二
# 按照上面的方法每次都要确保提供的初始值和数组长度一致,一般情况下我们可以让编译器根据初始值的个数自行推断数组的长度,例如:
func main() {
var testArray [3]int
var numArray = [...]int{1, 2}
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2]
fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int
fmt.Println(cityArray) //[北京 上海 深圳]
fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string
}
###############################
# 7.2.3.方法三
# 我们还可以使用指定索引值的方式来初始化数组,例如:
func main() {
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]
fmt.Printf("type of a:%T\n", a) //type of a:[4]int
}
################################################################
# 7.3.数组的遍历
# 遍历数组a有以下两种方法
func main() {
var a = [...]string{"北京", "上海", "深圳"}
// 方法1:for循环遍历
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
// 方法2:for range遍历
for index, value := range a {
fmt.Println(index, value)
}
}
################################################################
# 7.4.多维数组
# Go语言是支持多维数组的,我们这里以二维数组为例(数组中又嵌套数组)。
##################################
# 7.4.1.二维数组的定义
func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
fmt.Println(a) //[[北京 上海] [广州 深圳] [成都 重庆]]
fmt.Println(a[2][1]) //支持索引取值:重庆
}
##################################
# 7.4.2.二维数组的遍历
func main() {
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s\t", v2)
}
fmt.Println()
}
}
# 输出:
# 北京 上海
# 广州 深圳
# 成都 重庆
#
# 注意: 多维数组只有第一层可以使用...来让编译器推导数组长度。例如:
//支持的写法
a := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
//不支持多维数组的内层使用...
b := [3][...]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
##################################
# 7.5.数组是值类型
# 数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
func modifyArray(x [3]int) {
x[0] = 100
}
func modifyArray2(x [3][2]int) {
x[2][0] = 100
}
func main() {
a := [3]int{10, 20, 30}
modifyArray(a) //在modify中修改的是a的副本x
fmt.Println(a) //[10 20 30]
b := [3][2]int{
{1, 1},
{1, 1},
{1, 1},
}
modifyArray2(b) //在modify中修改的是b的副本x
fmt.Println(b) //[[1 1] [1 1] [1 1]]
}
# 注意:
# 1.数组支持 “==“、”!=” 操作符,因为内存总是被初始化过的。
# 2.[n]*T表示指针数组,*[n]T表示数组指针 。
8.Go语言fmt.Printf使用指南
########################################################################################################################
# fmt标准库是我们在学习Go语言过程中接触最早最频繁的一个了,本文介绍了fmtb包的一些常用函数。
#################################################################
# 8.1.fmt
# fmt包实现了类似C语言printf和scanf的格式化I/O。主要分为向外输出内容和获取输入内容两大部分。
#################################
# 8.1.1.向外输出
# 标准库fmt提供了以下几种输出相关函数。
#################
# 8.1.1.1.Print
# Print系列函数会将内容输出到系统的标准输出,区别在于Print函数直接输出内容,Printf函数支持格式化输出字符串,Println函数会在输出内容的结尾添加一个换行符。
func Print(a ...interface{}) (n int, err error)
func Printf(format string, a ...interface{}) (n int, err error)
func Println(a ...interface{}) (n int, err error)
# 举个简单的例子
func main() {
fmt.Print("在终端打印该信息。")
name := "沙河小王子"
fmt.Printf("我是:%s\n", name)
fmt.Println("在终端打印单独一行显示")
}
# 执行上面的代码输出
# 在终端打印该信息。我是:沙河小王子
# 在终端打印单独一行显示
#################
# 8.1.1.2.Fprint
# Fprint系列函数会将内容输出到一个io.Writer接口类型的变量w中,我们通常用这个函数往文件中写入内容。
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
# 举个例子:
// 向标准输出写入内容
fmt.Fprintln(os.Stdout, "向标准输出写入内容")
fileObj, err := os.OpenFile("./xx.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("打开文件出错,err:", err)
return
}
name := "沙河小王子"
// 向打开的文件句柄中写入内容
fmt.Fprintf(fileObj, "往文件中写如信息:%s", name)
# 注意,只要满足io.Writer接口的类型都支持写入。
#################
# 8.1.1.3.Sprint
# Sprint系列函数会把传入的数据生成并返回一个字符串。
func Sprint(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string
func Sprintln(a ...interface{}) string
# 简单的示例代码如下:
s1 := fmt.Sprint("沙河小王子")
name := "沙河小王子"
age := 18
s2 := fmt.Sprintf("name:%s,age:%d", name, age)
s3 := fmt.Sprintln("沙河小王子")
fmt.Println(s1, s2, s3)
#################
# 8.1.1.4.Errorf
# Errorf函数根据format参数生成格式化字符串并返回一个包含该字符串的错误。
# func Errorf(format string, a ...interface{}) error
# 通常使用这种方式来自定义错误类型,例如:
# err := fmt.Errorf("这是一个错误")
# Go1.13版本为fmt.Errorf函数新加了一个%w占位符用来生成一个可以包裹Error的Wrapping Error。
e := errors.New("原始错误e")
w := fmt.Errorf("Wrap了一个错误%w", e)
#################################
# 8.2.格式化占位符
# *printf系列函数都支持format格式化参数,在这里我们按照占位符将被替换的变量类型划分,方便查询和记忆。
#################
# 8.2.1.通用占位符
# 占位符 说明
# %v 值的默认格式表示
# %+v 类似%v,但输出结构体时会添加字段名
# %#v 值的Go语法表示
# %T 打印值的类型
# %% 百分号
#
# 示例代码如下
fmt.Printf("%v\n", 100)
fmt.Printf("%v\n", false)
o := struct{ name string }{"小王子"}
fmt.Printf("%v\n", o)
fmt.Printf("%#v\n", o)
fmt.Printf("%T\n", o)
fmt.Printf("100%%\n")
# 输出结果如下
100
false
{小王子}
struct { name string }{name:"小王子"}
struct { name string }
100%
#################
# 8.2.2.布尔型
# 占位符 说明
# %t true或false
#################
# 8.2.3.整型
# 占位符 说明
# %b 表示为二进制
# %c 该值对应的unicode码值
# %d 表示为十进制
# %o 表示为八进制
# %x 表示为十六进制,使用a-f
# %X 表示为十六进制,使用A-F
# %U 表示为Unicode格式:U+1234,等价于”U+%04X”
# %q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
#
# 示例代码如下
n := 65
fmt.Printf("%b\n", n)
fmt.Printf("%c\n", n)
fmt.Printf("%d\n", n)
fmt.Printf("%o\n", n)
fmt.Printf("%x\n", n)
fmt.Printf("%X\n", n)
# 输出结果如下
1000001
A
65
101
41
41
#################
# 8.2.4.浮点数与复数
占位符 说明
%b 无小数部分、二进制指数的科学计数法,如-123456p-78
%e 科学计数法,如-1234.456e+78
%E 科学计数法,如-1234.456E+78
%f 有小数部分但无指数部分,如123.456
%F 等价于%f
%g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
#
# 示例代码如下
f := 12.34
fmt.Printf("%b\n", f)
fmt.Printf("%e\n", f)
fmt.Printf("%E\n", f)
fmt.Printf("%f\n", f)
fmt.Printf("%g\n", f)
fmt.Printf("%G\n", f)
# 输出结果如下
6946802425218990p-49
1.234000e+01
1.234000E+01
12.340000
12.34
12.34
#################
# 8.2.5.字符串和[]byte
# 占位符 说明
# %s 直接输出字符串或者[]byte
# %q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
# %x 每个字节用两字符十六进制数表示(使用a-f
# %X 每个字节用两字符十六进制数表示(使用A-F)
#
# 示例代码如下
s := "小王子"
fmt.Printf("%s\n", s)
fmt.Printf("%q\n", s)
fmt.Printf("%x\n", s)
fmt.Printf("%X\n", s)
# 输出结果如下
小王子
"小王子"
e5b08fe78e8be5ad90
E5B08FE78E8BE5AD90
#################
# 8.2.5.指针
# 占位符 说明
# %p 表示为十六进制,并加上前导的0x
#
# 示例代码如下
a := 10
fmt.Printf("%p\n", &a)
fmt.Printf("%#p\n", &a)
# 输出结果如下
0xc000094000
c000094000
#################################
# 8.2.宽度标识符
# 宽度通过一个紧跟在百分号后面的十进制数指定,如果未指定宽度,则表示值时除必需之外不作填充。精度通过(可选的)宽度后跟点号后跟的十进制数指定。
# 如果未指定精度,会使用默认精度;如果点号后没有跟数字,表示精度为0。举例如下:
# 占位符 说明
# %f 默认宽度,默认精度
# %9f 宽度9,默认精度
# %.2f 默认宽度,精度2
# %9.2f 宽度9,精度2
# %9.f 宽度9,精度0
#
# 示例代码如下
n := 12.34
fmt.Printf("%f\n", n)
fmt.Printf("%9f\n", n)
fmt.Printf("%.2f\n", n)
fmt.Printf("%9.2f\n", n)
fmt.Printf("%9.f\n", n)
# 输出结果如下
12.340000
12.340000
12.34
12.34
12
#################################
# 8.3.其他falg
# 占位符 说明
# ’+’ 总是输出数值的正负号;对%q(%+q)会生成全部是ASCII字符的输出(通过转义);
# ’ ‘ 对数值,正数前加空格而负数前加负号;对字符串采用%x或%X时(% x或% X)会给各打印的字节之间加空格
# ’-’ 在输出右边填充空白而不是默认的左边(即从默认的右对齐切换为左对齐);
# ’#’ 八进制数前加0(%#o),十六进制数前加0x(%#x)或0X(%#X),指针去掉前面的0x(%#p)对%q(%#q),对%U(%#U)会输出空格和单引号括起来的go字面值;
# ‘0’ 使用0而不是空格填充,对于数值类型会把填充的0放在正负号后面;
#
# 举个例子
s := "小王子"
fmt.Printf("%s\n", s)
fmt.Printf("%5s\n", s)
fmt.Printf("%-5s\n", s)
fmt.Printf("%5.7s\n", s)
fmt.Printf("%-5.7s\n", s)
fmt.Printf("%5.2s\n", s)
fmt.Printf("%05s\n", s)
# 输出结果如下
小王子
小王子
小王子
小王子
小王子
小王
00小王子
#################################
# 8.4.获取输入
# Go语言fmt包下有fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,可以在程序运行过程中从标准输入获取用户的输入。
#################
# 8.1.fmt.Scan
# 函数定签名如下
# func Scan(a ...interface{}) (n int, err error)
# 1.Scan从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
# 2.本函数返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因。
具体代码示例如下
func main() {
var (
name string
age int
married bool
)
fmt.Scan(&name, &age, &married)
fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
}
将上面的代码编译后在终端执行,在终端依次输入小王子、28和false使用空格分隔
$ ./scan_demo
小王子 28 false
扫描结果 name:小王子 age:28 married:false
fmt.Scan从标准输入中扫描用户输入的数据,将以空白符分隔的数据分别存入指定的参数。
#################
# 8.2.fmt.Scanf
# 函数签名如下
# func Scanf(format string, a ...interface{}) (n int, err error)
# 1.Scanf从标准输入扫描文本,根据format参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中。
# 2.本函数返回成功扫描的数据个数和遇到的任何错误。
#
# 代码示例如下
func main() {
var (
name string
age int
married bool
)
fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &married)
fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
}
# 将上面的代码编译后在终端执行,在终端按照指定的格式依次输入小王子、28和false。
# $ ./scan_demo
1:小王子 2:28 3:false
扫描结果 name:小王子 age:28 married:false
# fmt.Scanf不同于fmt.Scan简单的以空格作为输入数据的分隔符,fmt.Scanf为输入数据指定了具体的输入内容格式,只有按照格式输入数据才会被扫描并存入对应变量。
# 例如,我们还是按照上个示例中以空格分隔的方式输入,fmt.Scanf就不能正确扫描到输入的数据。
$ ./scan_demo
小王子 28 false
扫描结果 name: age:0 married:false
#################
# 8.3.fmt.Scanln
# 函数签名如下
# func Scanln(a ...interface{}) (n int, err error)
# 1.Scanln类似Scan,它在遇到换行时才停止扫描。最后一个数据后面必须有换行或者到达结束位置。
# 2.本函数返回成功扫描的数据个数和遇到的任何错误。
# 具体代码示例如下
func main() {
var (
name string
age int
married bool
)
fmt.Scanln(&name, &age, &married)
fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
}
# 将上面的代码编译后在终端执行,在终端依次输入小王子、28和false使用空格分隔。
$ ./scan_demo
小王子 28 false
扫描结果 name:小王子 age:28 married:false
# mt.Scanln遇到回车就结束扫描了,这个比较常用。
#################
# 8.4.bufio.NewReader
# 有时候我们想完整获取输入的内容,而输入的内容可能包含空格,这种情况下可以使用bufio包来实现。示例代码如下:
func bufioDemo() {
reader := bufio.NewReader(os.Stdin) // 从标准输入生成读对象
fmt.Print("请输入内容:")
text, _ := reader.ReadString('\n') // 读到换行
text = strings.TrimSpace(text)
fmt.Printf("%#v\n", text)
}
#################
# 8.5.Fscan系列
# 这几个函数功能分别类似于fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,只不过它们不是从标准输入中读取数据而是从io.Reader中读取数据。
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
#################
# 8.6.Sscan系列
# 这几个函数功能分别类似于fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,只不过它们不是从标准输入中读取数据而是从指定字符串中读取数据。
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
posted on 2020-03-27 11:12 herisson_pan 阅读(49) 评论(0) 收藏 举报
浙公网安备 33010602011771号