目录
概述
Go语言优势
Go适合用来做什么?
环境搭建
安装和配置
第01天(基本类型、流程控制)
1.1 变量和常量
1.1.1 声明变量:
1.1.2声明常量:
1.1.3 iota枚举
1.2 基本数据类型
1.3 fmt包的格式化输出输入
1.4 类型转换和别名
1.4.1 类型转换
1.4.2 类型别名
1.5 运算符
1.6 流程控制
1.6.1 if语句
1.6.2 switch
1.6.3 for循环
1.6.4 Range迭代
1.6.5 break和continue的区别
第02天(函数、工程管理)
2.1 自定义函数
2.1.1 无参/有参 无返回值的函数
2.1.2 有返回值的函数
2.2 递归函数
2.3 函数类型
2.4 匿名函数与闭包
2.5 延迟调用defer
2.6 获取命令行参数
2.7 工程管理
2.7.1 工作区
2.7.2 导入包
第03天(复合类型)
3.1 指针
3.2 数组
3.3 slice切片
3.3.1 append函数
3.3.2 copy函数
3.3.3 切片的值传递
3.4 map
3.4.1 map的创建与初始化
3.4.2 map遍历和删除元素
3.5 结构体
3.5.1 结构体的创建
结构体的继承
3.5.2 结构体的值传递
3.5.3 可见性规则
第04天(面向对象编程)
4.1 匿名组合
4.2 结构体的方法
4.3 接口
4.3.1 创建
4.3.2 接口继承
4.3.3 通过if / switch实现类型断言
第05天(异常、文本文件处理)
5.1 error接口、panic、recover
5.2 字符串处理
5.3 正则表达式
5.4 JSON处理
5.5 文件操作
第06天(并发编程)
6.1 概述
6.2 goroutine
6.3 channel
6.4 select
第07天(网络概述、socket编程)
7.1 网络概述
7.2 Socket编程
7.3 案例:并发的聊天室服务器
第08天(HTTP编程)
8.1 Web工作方式
8.2 Http报文格式
8.3 Http编程
8.4 案例:网络爬虫
Go语言是云计算时代的C语言。专门针对多处理器系统应用程序的编程进行了优化,会用Go编译的程序可以媲美C或C++的速度,而且更加安全,支持并行进程。Go不仅提供了高性能的语言,同时也让开发更快速。
GO语言中文社区,GO语言标准库
配置Go语言开发运行环境(环境变量是自动配置的)。参考教程
Hello.go
// 1)go语言以包作为管理单位。
// 2)每个文件必须先声明包。
// 3)程序必须有一个main包。
package main
import "fmt"
// 入口函数 main(),左括号必须与函数名同行
func main() {
//打印。Println() 会自动换行。
//调用函数,大部分都需要导入包。
// go语言语句结尾没有分号。
fmt.Printf("hello world\n")
}
命令行运行程序
# 格式化go文件
go fmt 01_Hello.go
# 编译,成功后会生成exe可执行文件
go build 01_Hello.go
# 运行
.\01_Hello.exe
# 编译运行,但不生成exe可执行文件
go run 01_Hello.go
// 变量名是a,类型是int(可以省略),初始化值是10(默认值是0)
var a int = 10
var b,c int
// 直接声明变量值,并根据 变量值 推导出变量的类型
var c1 = 20
fmt.Printf("c1=%d",c1)
d := 30
fmt.Printf("d type is %T",d)
// 多重复值
e,f,g := 10,20,30
// 交换两个变量的值
i,j := 10,20
i,j = j,i
fmt.Printf("i=%d,j=%d\n",i,j)
const a int = 10
const b = 3.14
func main() {
const {
a=iota // 0
b=iota // 1
c=iota // 2
}
fmt.Printf("a=%d,b=%d,c=%d\n",a,b,c)
const {
i = iota // 0
j1,j2,j3 = iota,iota,iota // 1
k = iota // 2
}
}
类型 | 名称 | 长度 | 零值/初始值 | 说明 |
---|---|---|---|---|
bool | 布尔类型 | 1 | false | true或false |
byte | 字节型 | 1 | 0 | uint8别名 |
rune | 字符类型 | 4 | 0 | 专用于鵆unicode编码,等价于uint32 |
int,uint | 整型 | 4或8 | 0 | 32位或64位 |
int8,uint8 | 整型 | 1 | 0 | -128~127,0~255 |
int16,uint16 | 整型 | 2 | 0 | -32768~32767,0~65535 |
int32,uint32 | 整型 | 4 | 0 | -21亿~21亿,0~42亿 |
int64,uint64 | 整型 | 8 | 0 | |
float32 | 浮点数 | 4 | 0.0 | 小数位精确到7位 |
float64 | 浮点数 | 8 | 0.0 | 小数位精确到15位 |
complex64 | 复数类型 | 8 | ||
complex128 | 复数类型 | 16 | ||
uintptr | 整型 | 4或8 | 足以存储指针的uint32或uint64整数 | |
string | 字符串 | "" | utf-8字符串 |
输入格式
func main() {
var a int
fmt.Printf("请输入变量a: ")
// fmt.Scanf("%d",&a)
fmt.Scan(&a)
fmt.Println("a=",a)
}
格式 | 含义 |
---|---|
%% | 一个%字面量 |
%b | 一个二进制整数值(基数为2,)或者是一个(高级的)用科学计数法表示的指数为2的浮点数 |
%c | 字符型,可以把数字按照ASCII码转成对应的字符 |
%d | 十进制数 |
%e | 科学计数法,e表示浮点数或者复数值 |
%E | 科学计数法,E表示浮点数或者复数值 |
%f | 标准计数法表示的浮点数或复数值 |
%g,%G | 以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
%o | 八进制数 |
%p | 十六进制数,前缀为0x,字符是小写的a-f |
%q | 转义 |
%s | 字符串 |
%t | 布尔值,true或false |
%T | 值的类型 |
%U | Unicode编码表示的整型码点,默认为4个数字字符 |
%v | 使用默认格式输出的内置或自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话。 |
%x,%X | 十六进制证书,分别是小写字母表示、大写字母表示 |
func main() {
ch := 'c'
var a = int(ch)
fmt.Println("a=",a)
}
type bigint int64 //int64类型改名为bigint
var x bigint = 100
type {
myint int //int改名为myint
mystr string //string改名为mystr
}
略。
func main() {
// if
var a = 3
if a == 3 {
fmt.Println("a==3")
}
// if 中的 变量定义
if b := 3; b == 3 {
fmt.Printf("b==3\n")
}
// if-else
if x := 3; x == 4 {
fmt.Println("a==4")
} else {
fmt.Println("a!=4")
}
// if-elseif-if
if y := 0; y == 3 {
fmt.Println("y==0")
} else if y == 1 {
fmt.Println("y==1")
} else {
fmt.Println("y!=0或1")
}
}
func main() {
var num int
fmt.Printf("请输入num的值:")
fmt.Scan(&num)
switch num {
case 1:
fmt.Println("num=1")
break
case 2:
fmt.Println("num=2")
break
case 3:
fmt.Println("num=3")
break
default:
fmt.Println("num错误")
}
}
func main() {
sum:=0
for i:=1;i<=100;i++{
sum += i
}
fmt.Println("sum=",sum)
}
func main() {
str := "abc"
//迭代打印,默认返回两个值:元素位置、元素本身
for i,data := range str{
fmt.Printf("str[%d]=%c\n",i,data)
}
// 第2个返回值默认丢弃,只有i接收到元素位置
for i := range str{
fmt.Printf("str[%d]\n",i)
}
}
func main() {
i := 0
for{ // 死循环
i++
if i==5{
// 跳过之后的步骤,继续执行循环体
continue
}
if i==10{
// 打破/跳出循环体
break
}
fmt.Println("i=",i)
}
}
函数组成:
package main
import "fmt"
// 无参无返回值
func fun1(){
a:=6
fmt.Println("a=",a)
}
// 有参无返回值
func fun2(a int){
fmt.Println("a=",a)
}
// 不定参数 函数
func fun3(a int,args ... int){
fmt.Printf("a = %d \n",a)
for i,data := range args {
fmt.Printf("args[%d]=%d \n",i,data)
}
}
// 不定参数函数 的 不定参 传递
func fun4(args ... int){
//fun3(args[0],args ...)
// 从第0个元素开始,传递前2个元素
//fun3(args[0],args[:2] ...)
// 从第2个元素开始,传递包括第2个元素以及之后的所有元素
fun3(args[0],args[2:] ...)
}
func main() {
//fun1()
//fun2(2)
//fun3(1,2,3)
fun4(9,8,7,6)
}
// 需要 给 返回值 声明一个变量值
func fun1() (result int) {
result = 6
return
}
// 多个返回值
func fun2() (a int,b int,c int){
a,b,c = 1,2,3
return
}
func main() {
a := fun1()
fmt.Println("a=",a)
x,y,_ := fun2()
fmt.Printf("x=%d,y=%d,",x,y)
_,_,z := fun2()
fmt.Println("z=",z)
}
func getMax(a, b int) (max, min int) {
if a >= b {
max = a
min = b
} else {
max = b
min = a
}
return
}
func main() {
max, min := getMax(10, 20)
fmt.Printf("max=%d,min=%d", max, min)
}
func fibonacci(item int) (result int) {
if item == 1 || item == 2 {
result = 1
return
} else if item > 2 {
result = fibonacci(item-1) + fibonacci(item-2)
return
} else {
result = -1
return
}
}
func main() {
item := 20
result := fibonacci(item)
fmt.Printf("斐波那契的第%d个元素是:%d", item, result)
}
// 函数类型,函数也是一种数据类型。
// type 关键字,FuncType 是一个函数类型的自定义名称
type FuncType func(int, int) int
func Add(a,b int) int{
return a+b
}
func Minus(a,b int) int{
return a-b
}
// 回调函数,函数有一个参数是 函数类型,这个函数就是回调函数。声明一个 fTest 的函数类型作为形参
// 实现一个计算器,可以进行四则运算
// 多态
func Calc(a, b int, fTest FuncType) (result int) {
fmt.Println("Calc")
result = fTest(a, b)
return
}
func main() {
a := Calc(1,1,Add)
//a := Calc(1,1,Minus)
fmt.Println("a=",a)
}
func main() {
// 定义匿名函数,并调用 打印变量值
func1 := func(a int,str string){
fmt.Println("a=",a)
fmt.Println("str=",str)
}
func1(1,"Jack")
}
func main() {
a := 10
str := "Milk"
func() {
// 闭包 是 以 引用方式 获取外部变量的,当引用值被改变后,闭包外部的值也会被改变
a = 6
str = "go"
fmt.Printf("【内部】a=%d,str=%s\n", a, str)
}() // ()表示 直接调用
fmt.Printf("【外部】a=%d,str=%s\n", a, str)
}
func fun() func() int {
var x int
return func() int {
// 闭包中的变量,不会因为重新调用函数而被重新初始化
x++
return x * x
}
}
func main() {
result := fun()
fmt.Println("result=",result())
fmt.Println("result=",result())
fmt.Println("result=",result())
fmt.Println("result=",result())
}
关键字 defer 用于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行。注意,defer语句只能出现爱你在函数或方法的内部。
defer语句经常被用于处理成对的操作,比如开&关,连接&断开,加锁&释放锁等。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放,释放资源的defer应该直接跟在请求资源的语句后。
func main() {
defer fmt.Println("延迟执行,main函数执行结束后才被执行。")
fmt.Println("直接被执行。")
}
多个defer的执行顺序
先进后出,写在前边的defer,最后被执行。哪怕之前有函数发生了错误,defer修饰的方法也会被执行。
defer和匿名函数结合使用
func main() {
a := 10
b := 20
defer func() {
fmt.Printf("【内部】a=%d, b=%d\n",a,b)
}()
a=111
b=222
fmt.Printf("【外部】a=%d, b=%d\n",a,b)
}
需要以命令行的形式执行,因为要获取 命令行输入的参数。idea打开terminal,执行 go run 01_Hello.go a b c d
func main() {
// 接收用户传递的参数,都是以字符串方式传递的
list := os.Args
for i,data := range list{
fmt.Printf("lislt[%d]=%s\n",i,data)
}
}
Go代码必须放在工作区中。工作区是一个对应于特定工程的目录,包含三个子目录:src目录、pkg目录和bin目录。
GoPath设置:为了能构建工程,需要先把所需工程的概目录加入到环境变量GoPath中,否则即使处于同一工作目录,代码之间也无法通过绝对代码包路径完成调用。
实际开发环境中,工作目录往往有多个,这些工作目录的路径都需要添加至GoPath。有多个目录时,需注意分隔符(Windows是分好,Linux是冒号),当有多个GoPath时,默认会将go get的内容放到第一个目录下。
导包的方式
所有Go语言的程序都会组织成多干组文件,每组文件被称为一个包。包的代码可以作为最小复用单元,被其他项目引用。
//点操作。在调用包中的函数时,可以省略前缀
import {
. "fmt"
}
func main() {
Println("Hello go")
}
// 别名操作,调用的包可以自定义别名
import {
io "fmt"
}
func main() {
io.Println("Hello go")
}
// 忽略导入包。有时用户可能需要导入一个包,但是不需要引用这个包的标识符。此时可以使用空白标识符_来重命名这个导入包。
// _操作其实是引入该包,但不直接使用包里的函数,而是调用了该包里面的init函数,init函数下面就介绍
import {
_ "fmt"
}
func main() {
}
包(类似于Java的类)中的 init函数和main函数
在main入口文件中,导入其它包时,会先执行该包的init函数,其它函数只有调用时才会执行。
类型 | 名称 | 长度 | 默认值 | 说明 |
---|---|---|---|---|
pointer | 指针 | nil | ||
array | 数组 | 0 | ||
slice | 切片 | nil | 引用类型 | |
map | 字典 | nil | 引用类型 | |
struct | 结构体 |
// 值传递
func swap1(a, b int) {
a, b = b, a
fmt.Printf("【swap】a=%d,b=%d\n", a, b)
}
// 指针地址传递,交换值
func swap2(a, b *int) {
*a, *b = *b, *a
fmt.Printf("【swap】a=%d,b=%d\n", *a, *b)
}
func main() {
a, b := 10, 20
swap2(&a, &b)
fmt.Printf("【main】a=%d,b=%d\n", a, b)
}
func main() {
// 创建随机数,设置种子。只需设置一次。以当前系统时间作为种子参数
rand.Seed(time.Now().UnixNano())
var a [10]int
for i:=0; i<10;i++ {
//随机数很大
//a[i] = rand.Int()
//限定随机数的范围
a[i] = rand.Intn(10)
}
fmt.Println("a=",a)
// 二维数组
var b [3][4]int
for i := 0; i < 3; i++ {
for j := 0; j < 4; j++ {
b[i][j] = i + j
}
}
fmt.Println("b=",b)
c := [2][3] int {
{1,2,3},
{2,3,4},
}
fmt.Println("c=",c)
}
// 冒泡排序,值传递
func bubble(a [5]int) {
fmt.Println("【排序前】", a)
for i := 0; i < len(a); i++ {
for j := i + 1; j < len(a); j++ {
if a[i] > a[j] {
a[i], a[j] = a[j], a[i]
}
}
}
fmt.Println("【排序后】", a)
}
// 冒泡排序,指针引用传递
func pointer(a *[5]int) {
for i := 0; i < len(a); i++ {
for j := i + 1; j < len(a); j++ {
if a[i] > a[j] {
a[i], a[j] = a[j], a[i]
}
}
}
}
func main() {
a := [5]int{5, 3, 8, 6, 9}
//bubble(a)
fmt.Println("【排序前】", a)
pointer(&a)
fmt.Println("【排序后】", a)
}
切片:通过内部指针和相关属性引用数组片段,已实现数组变长方案。弥补数组的定长。
// 切片的基础,main函数
func mainBackup() {
// 切片的创建过程中,[]是空的,表示长度不固定。
a := []int{1, 2, 3, 4, 5}
// 切片截取时,格式为[low:high:max],下标从low开始,截取到下标high(不包括),容量cap=max-low。
// low、high的下标值必须 在 切片a 元素下标的范围内,并且 max >= high-low && max < len(a)
// 下例中,cap容量: 5-1=4。
s := a[1:4:5]
fmt.Println("s=", s)
fmt.Printf("len(s)=%d, cap(s)=%d\n", len(s), cap(s))
y := []int{}
// 切片中 添加元素
y = append(s, 11)
fmt.Println(y)
}
// 创建切片
func main() {
//创建切片 1
a := []int{1,2,3,4,5}
fmt.Println(a)
// 创建切片 2
b := make([]int,5,10)
fmt.Printf("len(b)=%d, cap(b)=%d \n",len(b),cap(b))
fmt.Println(b)
}
切片截取的常用方法:
操作 | 含义 |
---|---|
s[n] | 切片s中索引位置为n的元素 |
s[:] | 切片s的索引位置0 到 len(s)-1 处 截取的切片 |
s[low:] | 切片s的索引位置 low 到 len(s)-1 处 截取的切片 |
s[:high] | 切片s的索引位置 0 到 high 处 截取的切片 |
s[low:high] | 切片s的索引位置 low 到 high 处 截取的切片 |
s[low:high:max] | 切片s的索引位置 low 到 high 处 截取的切片,长度len=high-low,容量cap=max-low |
len(s) | 切片s的长度,总是<=cap(s) |
cap(s) | 切片s的容量,总是>=len(s) |
向切片的末尾添加元素。
特点:扩容时,若超过底层数组的容量长度,通常以2倍的容量扩容,并复制原来的数组到新的底层数组中。
func main() {
a := []int{1, 2, 3}
a = append(a, 5)
a = append(a, 5)
a = append(a, 5)
fmt.Printf("len=%d, cap=%d\n", len(a), cap(a))
fmt.Println("[a]=", a)
}
copy可以在两个slice间复制数据,复制长度以len小的为准,两个slice可指向同一底层数组。
func main() {
data := [...]int{0,1,2,3,4,5,6,7,8,9}
a := data[8:]
b := data[:5]
//将a切片的值,拷贝到b切片中,并覆盖掉b原先的值。
copy(b,a)
fmt.Println(b)
}
//初始化切片
func InitData(s []int) {
rand.Seed(time.Now().UnixNano())
for i := 0; i < len(s); i++ {
s[i] = rand.Intn(100)
}
}
func BubbleSort(s []int) {
n := len(s)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if s[j] > s[j+1] {
s[j], s[j+1] = s[j+1], s[j]
}
}
}
}
func main() {
n := 10
s := make([]int, n)
InitData(s)
fmt.Println("排序前:", s)
BubbleSort(s)
fmt.Println("排序后:", s)
}
func main() {
var m map[int]string
fmt.Printf("len=%d\t", len(m))
fmt.Println("m=", m)
m2 := make(map[int]string)
for i := 0; i < 10; i++ {
m2[i] = "Jack" + strconv.Itoa(i)
}
fmt.Println("m2=", m2)
m3 := make(map[string]string, 2)
m3["a"] = "Java"
m3["b"] = "Go"
m3["c"] = "C"
fmt.Println("len=", len(m3), "\t m3=", m3)
m4 := map[int]string{1: "python", 2: "C++", 3: "JavaScript"}
fmt.Println("m4=", m4)
}
// map作为函数参数传递
func operator(m map[int]string){
// map 删除key-value
delete(m,3)
fmt.Println("m=",m)
}
func main() {
m := make(map[int]string)
for i := 0; i < 10; i++ {
m[i] = "Jack" + strconv.Itoa(i)
}
//遍历,无序的
for key,value := range m{
fmt.Printf("%d—>%s \n",key,value)
}
operator(m)
}
type Student struct {
id int
name string
sex byte
age int
addr string
}
func main() {
var stu = Student{1, "Jack", 'm', 18, "北京"}
fmt.Println("stu=", stu)
var stu2 = Student{name: "Michael", addr: "天津"}
fmt.Println("stu2=", stu2)
stu3 := &Student{age: 22, id: 101, name: "Mike"}
fmt.Println("stu3=", *stu3)
stu3.id = 102
fmt.Println("stu3.id=", stu3.id)
}
type Person struct {
name string
sex byte
age int
}
type Student struct {
id int
Person
addr string
}
func main() {
//var stu = Student{128,Person{"Jack",'w',22},"河南"}
var stu = Student{Person: Person{name: "Michael"}, addr: "天津"}
fmt.Println("stu=", stu)
}
type Student struct {
id int
name string
sex byte
age int
addr string
}
func test1(stu Student){
stu.id=102
fmt.Println("【test1】stu=", stu)
}
func test2(stu *Student){
stu.id=102
fmt.Println("【test2】stu=", stu)
}
func main() {
var stu = Student{name: "Michael", addr: "天津"}
//test1(stu)
test2(&stu)
fmt.Println("【main】stu=", stu)
}
可见性:类似于Java中的public、private、protected的概念。如果想使用其他包的函数、结构体类型、结构体成员、函数名、类型名、结构体成员变量名,那么其首字母必须大写,才能可见。若是小写,只能在同一个包中使用。
略。
type Person struct {
name string
sex byte
age int
}
// 方法:结构体的 打印函数
func (tmp Person) PrintInfo() {
fmt.Println("tmp=", tmp)
}
// 结构体的 初始化赋值 函数
func (p *Person) SetInfo(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
}
func main() {
var p Person
(&p).SetInfo("Mike", 'm', 28)
p.PrintInfo()
}
type Person struct {
name string
sex byte
age int
}
// 接收者 为 普通变量, 值传递
func (p Person) SetInfoValue(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
}
// 接收者为 指针变量 , 引用传递
func (p *Person) setInfoPointer(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
}
func main() {
p := Person{"Jack",'m',12}
fmt.Println("&p=",&p) // 打印地址
p.SetInfoValue("Mike",'w',18)
fmt.Println("p=",p)
p.setInfoPointer("Rose",'w',27)
fmt.Println("p=",p)
(&p).setInfoPointer("Rose",'w',28)
fmt.Println("p=",p)
}
go中,接口命名习惯以er结尾。
空接口:不包含任何方法的接口。所有类型都可以认为是实现了空接口。可以存储任意类型的数值。
func main() {
// 创建一个空接口
i := make([]interface{}, 3)
i[0] = 1
i[1] = "hello"
i[2] = Student{25, "Jack"}
fmt.Println(i)
}
// 定义接口类型,并声明接口方法
type Humaner interface {
sayHi()
}
type Student struct {
id int
name string
}
// 实现 接口方法
func (tmp *Student) sayHi() {
fmt.Printf("Student[%s,%d] sayhi\n", tmp.name, tmp.id)
}
type Teacher struct {
addr string
group string
}
func (tmp *Teacher) sayHi() {
fmt.Printf("Teacher[%s,%s] sayhi\n", tmp.addr, tmp.group)
}
func main() {
var i Humaner
s := &Student{13, "Jack"}
i = s
i.sayHi()
t := &Teacher{"bj", "go"}
i = t
i.sayHi()
}
// 定义接口类型,并声明接口方法
type Humaner interface {
sayHi()
}
// 继承 Humaner接口
type Personer interface {
Humaner
sing(lrc string)
}
type Student struct {
id int
name string
}
// 实现 接口方法
func (tmp *Student) sayHi() {
fmt.Printf("Student[%s,%d] sayhi\n", tmp.name, tmp.id)
}
func (tmp *Student) sing(lrc string) {
fmt.Printf("Student在唱:%s\n", lrc)
}
func main() {
// 接口继承 案例
var i Personer
s := &Student{13, "Jack"}
i = s
i.sayHi()
i.sing("好久不见")
//接口转换
var hunamer Humaner
var personer Personer
personer = &Student{22,"Rose"}
hunamer = personer
hunamer.sayHi()
}
type Student struct {
id int
name string
}
func main() {
// 创建一个空接口
i := make([]interface{}, 3)
i[0] = 1
i[1] = "hello"
i[2] = Student{25, "Jack"}
// if 断言类型
for index,data := range i{
if value,ok := data.(int);ok==true{
fmt.Printf("i[%d] 类型为int,内容为%d\n",index,value)
}else if value,ok := data.(string);ok==true{
fmt.Printf("i[%d] 类型为string,内容为%s\n",index,value)
}else if value,ok := data.(Student);ok==true{
fmt.Printf("i[%d] 类型为Student,内容为 name=%s,id=%d\n",index,value.name,value.id)
}
}
// switch 断言类型
for index,data := range i{
switch value := data.(type) {
case int:
fmt.Printf("i[%d] 类型为int,内容为%d\n",index,value)
case string:
fmt.Printf("i[%d] 类型为string,内容为%s\n",index,value)
case Student:
fmt.Printf("i[%d] 类型为Student,内容为 name=%s,id=%d\n",index,value.name,value.id)
}
}
}
在Go语言中,程序的错误由内建的error接口支持,errors标准包提供了最基本的错误处理方法,用户还可自定义错误处理。而程序的异常处理通常由panic和recover实现触发和终止。
// Error接口
func createErr() {
err1 := fmt.Errorf("%s", "this is normal error")
fmt.Println("err1=", err1)
err2 := errors.New("this is normal err2")
fmt.Println("err2=", err2)
}
func MyDiv(a, b int) (result int, err error) {
if b == 0 {
err = errors.New("分母不能为0")
return
}
result = a / b
return
}
func main() {
createErr()
result, err := MyDiv(1, 1)
if err != nil {
fmt.Println("err=", err)
} else {
fmt.Println("result=", result)
}
}
当遇到不可恢复的错误状态,比如空指针引用、下标越界、向空map添加键值等,应该调用panic。当panic异常发生时,程序会中断运行,随后程序崩溃并输出日志信息,日志信息把包括panic value和函数调用的堆栈跟踪信息。
func test1() {
fmt.Println("aaaaa")
}
func test2() {
//主动调用 panic
panic("this is a panic test")
}
func test3() {
fmt.Println("ccccc")
}
// 数组越界
func test4(x int) {
var a [10]int
a[x] = 111
}
func main() {
//test1()
//test2()
//test3()
test4(20)
}
panic会导致程序崩溃(crash/终止运行)。
func test1() {
fmt.Println("aaaaa")
}
func test2(x int) {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var a [10]int
a[x] = 111
}
func test3() {
fmt.Println("ccccc")
}
func main() {
test1()
test2(11)
test3()
}
函数名 | 返回值 | 使用例子 | 说明 |
---|---|---|---|
contains | bool(true/false) | strings.Contains("seafood","food") | 字符串是否包含其他字符串 |
join | string | s:=[]string{"good","morning","Jack"} result:=strings.Join(s," ") fmt.Println(result) |
将数组中的字符串进行拼接 |
index | int | strings.Index("Morning", "ing") | 查找执行字符串所在的位置,不存在返回-1 |
repeat | string | "ba"+strings.Repeat("na",2) | 重复拼接字符串多次。 |
replace | string | strings.Replace("banana","na","bala",-1) | 字符串替换,n表示替换次数,小于0表示全部替换 |
split | slice切片 | strings.Split("Let us go to school"," ") | 字符串按照 某字符 进行分割,返回slice切片 |
trim | string | strings.Trim("oppo","o") | 头尾去除指定字符串 |
fields | slice切片 | strings.Fields(" it's to far from here ") | 去除字符串的空格符,并按照空格分割,返回slice切片类型 |
函数名 | 使用例子 | 说明 |
---|---|---|
Append系列函数 | func main() { str := make([]byte,0,100) // 初始化一个空数组 str = strconv.AppendInt(str,1234,10) // 以10进制方式追加 str = strconv.AppendQuote(str,"abcdefg") str = strconv.AppendQuoteRune(str,'崔') fmt.Println(string(str)) } |
将整数等转换为字符串后,添加到现有的字节数组中。 |
func main() {
var str string
str = strconv.FormatBool(false)
//'f'—— 打印格式:小数方式,-1——小数点位数(紧缩模式),64以float64处理
str = strconv.FormatFloat(3.14, 'f', -1, 64)
//整型转字符串,常用
str = strconv.Itoa(666)
fmt.Println("str=", str)
// 字符串转整型,常用
a,_ := strconv.Atoi("456")
fmt.Println("a=",a)
// 字符串转其他类型
var flag bool
var err error
flag, err = strconv.ParseBool("true")
if err == nil {
fmt.Println("flag=", flag)
} else {
fmt.Println("err=", err)
}
}
// 字符串匹配
func test1() {
buf := "abc azc a7c aac 888 a9c tac"
// 1. 解释规则,它会解析正则表达式,如果成功返回解释器
reg1 := regexp.MustCompile(`a.c`)
if reg1 == nil { // 解释失败,返回nil
fmt.Println("err=")
return
}
//2. 根据规则提取关键信息,获取符合 a*c 格式 的字符串。-1获取所有匹配的。
result1 := reg1.FindAllStringSubmatch(buf, 3)
fmt.Println("result1=", result1)
}
// 字符串中的 小数 匹配
func test2() {
buf := "43.15 568 asdf 1.25 3.14 7. 8.9 asqe 6.66 .5 8.9"
reg := regexp.MustCompile(`\d+\.\d+`)
if reg == nil {
fmt.Println("MustCompile err")
return
}
//result := reg.FindAllString(buf, -1)
// 提取后 分组
result := reg.FindAllStringSubmatch(buf,-1)
fmt.Println("result=", result)
}
func main() {
//test1()
test2()
}
type IT struct {
Company string `json:"公司"` // json的二次编码
Subjects []string
IsOK bool `json:"-"` // - 表示 转换成json时 忽略该字段
Price float64 `json:"价格"`
Age int `json:",string"` // 定义 JSON 输出的格式
}
// 结构体 转 JSON
func struct2json() {
//定义一个结构体变量
s := IT{"cuiCompany", []string{"java", "c", "c++"}, true, 100.35, 28}
//buf, err := json.Marshal(s)
// JSON格式化
buf, err := json.MarshalIndent(s, "", "")
if err != nil {
fmt.Println("err=", err)
return
}
fmt.Println("byf=", string(buf))
}
// JSON 转 结构体
func json2struct() {
jsonBuf := `{
"公司": "cuiCompany",
"Subjects": [
"java",
"c",
"c++"
],
"价格": 100.35,
"Age": "28"
}`
var tmp IT
err := json.Unmarshal([]byte(jsonBuf), &tmp)
if err != nil {
fmt.Println("err=", err)
return
}
fmt.Println("tmp=", tmp)
fmt.Printf("tmp=%+v\n", tmp)
}
func main() {
//struct2json()
json2struct()
}
// map 转 JSON
func map2json() {
m := make(map[string]interface{}, 4)
m["company"] = "cuiCompany"
m["isok"] = true
m["subject"] = []string{"go", "c", "java", "c++"}
m["price"] = 3.1415
//result, err := json.Marshal(m)
// json格式化
result, err := json.MarshalIndent(m, "", " ")
if err != nil {
fmt.Println("err=", err)
return
}
fmt.Println("result=", string(result))
}
// JSON 转 map
func json2map() {
jsonBuf := `{
"company": "cuiCompany",
"isok": true,
"price": 3.1415,
"subject": [
"go",
"c",
"java",
"c++"
]
}`
tmp := make(map[string]interface{}, 4)
err := json.Unmarshal([]byte(jsonBuf), &tmp)
if err != nil {
fmt.Println("err=", err)
return
}
fmt.Println("tmp=", tmp)
fmt.Printf("tmp=%+v\n", tmp)
// map类型断言
var str string
for key, value := range tmp {
switch data := value.(type) {
case string:
str = data
fmt.Printf("map[%s]类型是string,value=%s\n", key, str)
case bool:
fmt.Printf("map[%s]bool,value=%v\n", key, data)
case float64:
fmt.Printf("map[%s]float64,value=%v\n", key, data)
case []string:
fmt.Printf("map[%s][]string,value=%v\n", key, data)
case []interface{}:
fmt.Printf("map[%s][]interface,value=%v\n", key, data)
}
}
}
func main() {
//map2json()
json2map()
}
// 写入文件
func WriteFile(path string) {
//打开/新建文件
f, err := os.Create(path)
if err != nil {
fmt.Println("err=", err)
return
}
//关闭文件
defer f.Close()
var buf string
for i := 0; i < 10; i++ {
// "i=1" 字符串存储在buf中
buf = fmt.Sprintf("i = %d\n", i)
// 开始写入文件
n, err := f.WriteString(buf)
if err != nil {
fmt.Println("err=", err)
return
}
fmt.Println("n=", n)
}
}
// 读取文件
func ReadFile(path string) {
//打开文件
f, err := os.Open(path)
if err != nil {
fmt.Println("err=", err)
return
}
//关闭文件
defer f.Close()
buf := make([]byte, 1024*2) //2K大小
n, err1 := f.Read(buf)
if err1 != nil && err1 != io.EOF { //文件出错,并还没到文件结尾
fmt.Println("err1=", err1)
return
}
fmt.Println("buf=\n", string(buf[:n]))
}
// 按行读取文件
func ReadLine(path string) {
//打开文件
f, err := os.Open(path)
if err != nil {
fmt.Println("err=", err)
return
}
//关闭文件
defer f.Close()
// 新建缓冲区,暂时存放内容
r := bufio.NewReader(f)
for {
buf, err := r.ReadBytes('\n')
if err != nil {
if err == io.EOF { //文件结束
break
}
fmt.Println("err=", err)
}
fmt.Printf("buf=#%s#\n", string(buf))
}
}
func main() {
path := "./demo.txt"
//WriteFile(path)
//ReadFile(path)
ReadLine(path)
}
文件拷贝
从命令行输入 源文件、目的文件 参数,进行拷贝
PS E:\ideaWorkspace\HelloGo> go build .\01_Hello.go
PS E:\ideaWorkspace\HelloGo> .\01_Hello.exe .\demo.txt abc.txt
func main() {
list := os.Args //获取命令行参数
if len(list) != 3 {
fmt.Println("usage: xxx srcFile dstFile")
return
}
srcFile := list[1]
dstFile := list[2]
if srcFile == dstFile {
fmt.Printf("源文件和目的文件不能同名")
return
}
// 只读方式打开源文件
sF, err1 := os.Open(srcFile)
if err1 != nil {
fmt.Println("err1=", err1)
return
}
//新建目的文件
dF, err2 := os.Create(dstFile)
if err2 != nil {
fmt.Println("err2=", err2)
return
}
// 关闭文件
defer sF.Close()
defer dF.Close()
// 读取源文件内容,写入目的文件内容
buf := make([]byte, 4*1024) //4K的 临时缓冲区
for {
n, err := sF.Read(buf)
if err != nil {
if err == io.EOF { //读取完毕
break
}
fmt.Println("err=", err)
}
// 写
dF.Write(buf[:n])
}
}