go的基础
go打包
go build -gcflags="all=-N -l" -o demo.exe
引用类型
1.切片
2.map
3.channel
4.interface
5.func
6.指针类型
关键词或保留字
1.break 跳转语句,跳出循环或跳出switch语句,可跳转到指定的 标签位置
2.default 默认选项,switch或select默认操作项
3.func 定义一个函数
4.interface 声明一个接口
5.select golang语言层面I/O多路复用机制,用于检测管道是否就绪,与case和default一起使用
6.case 用于switch或select语句块,case用于指定一个或多个值(常量或者表达式)表示满足条件方可执行其中的语句块
7.defer 方法延迟调用关键词
8.go 启动协程的关键词
9.map 集合类型
10.struct 结构体类型
11.chan 通道类型
12.else 条件表达式 否则
13.goto 跳转到某一个 标签位置
14.package 声明包名
15.switch 流程控制语句,根据不同的条件执行不同的语句块,与 case 和 default 一起使用
16.const 声明常量
17.fallthrough 表示通过当前语句块,switch语句中表示可以执行下一个语句块
18.if 条件表达式 如果
19.range 用于for循环, 循环数组、切片、集合、管道等类型,获取其中元素、索引、键值
20.type 类型定义的关键词,声明一个类型
21.continue 与for循环使用,跳出当次循环
22.for 循环语句
23.import 包导入语句
24.return 方法返回语句
25.var 变量声明的关键词
预定义表示
1.append 附加,向切片附加元素
2.cap 获取容量,数据、切片、通道的容量
3.close 关闭通道
4.copy 用于切片的拷贝
5.imag 返回复数的虚部
6.real 返回复数的实部
7.panic 抛出异常消息
8.recover 恢复因异常中断的协程,并返回异常消息
9.iota 常量计数器,可与常量配合使用,实现美剧的长度
10.len 获取数组、切片、字符串、通道等类型的长度
11.make 用于初始化 切片、集合、通道 并返回其对象
12.new 创建一个类型的变量并为其分配内存空间,并返回类型的指针,常用语结构体变量的创建
13.nil 引用类型的零值
14.false,true 布尔类型值
15.print 打印消息,不换行
16.println 打印并换行
17.bool,byte
18.complex,complex64,complex128
19.float32,float64
20.int,uint,int8,int16,int32,uint32,int64,uint64
21.string
22.uintptr
数据类型与变量
1.bool
2.string
3.byte
4.int,uint,int8,uint8,int16,uint16,int32,uint32,int64,uint64
5.float32,float64,complex,complex64,complex128
6.rune
7.uintptr 无符号整型,用于存放一个指针,该类型用于指针计算
8.结构类型
9.指针类型 *string
10.数组
11.切记
12.map
13.interface{}
14.通道类型
15.函数类型
16.时间类型
变量定义
package _case
import "fmt"
func VarDeclareCase() {
//通过var关键词声明变量
var i, z, x int = 1, 2, 3
// 通过var关键词声明变量,并赋值
var j int = 100
// 通过var关键词声明变量,并赋值
var f float32 = 100.123
// 通过:= 以推断的方式定义变量并赋值,此方式只能用于局部变量的定义
b := true
// 数组
var arr = [5]int{1, 2, 3, 4, 5}
arr1 := [...]int{2, 3, 4, 5, 6}
var arr2 [5]int
arr2[2] = 4
arr2[3] = 5
fmt.Println(i, z, x, j, f, b, arr, arr1, arr2)
// 指针类型,用于表示变量地址的类型
var intPtr *int
var floatPtr *float64
var i1 = 100
f1(&i1)
//接口类型
var inter interface{}
inter = i1
inter = f
fmt.Println(intPtr, floatPtr, i1, inter)
}
func f1(i *int) {
*i = *i + 1
}
// 输出结构
1 2 3 100 100.123 true [1 2 3 4 5] [2 3 4 5 6] [0 0 4 5 0]
<nil> <nil> 101 100.123
常量定义
package _case
import "fmt"
const (
B = 1 << (10 * iota)
KB
MB
_
TB
)
type Gender = uint
const (
FEMALE Gender = iota
MALE
THIRD
)
func ConstAndEnumCase() {
const (
A = 10
B = 20
)
size := MB
var gender Gender = MALE
fmt.Println(A, B, gender, size)
}
结构体类型
package _case
import "fmt"
type user struct {
Name string
Age uint
Addr Address
}
type Address struct {
Province string
City string
}
func StructCase() {
//值类型
u := user{
Name: "nick",
Age: 18,
}
f2(u)
fmt.Println(u)
//指针类型
u1 := &user{
Name: "nick1",
Age: 19,
}
fmt.Println(u1)
//指针类型
u2 := new(user)
u2.Name = "nick3"
u2.Age = 20
fmt.Println(u2, u2.Addr.Province, u2.Addr.City)
// 结构体为值类型,定义变量后将被默认初始化
var u3 user
fmt.Println(u3)
}
func f2(u user) {
u.Age = 28
}
new与make
package _case
import "fmt"
func NewCase() {
// 通过new函数,可以创建任意类型,并返回指针
mpPtr := new(map[string]*user)
//(*mpPtr)["A"] = &user{}
if mpPtr == nil {
fmt.Println("mpPtr nil")
}
slicePtr := new([]user)
if *slicePtr == nil {
fmt.Println("slicePtr nil", *slicePtr)
}
*slicePtr = append(*slicePtr, user{Name: "Alice"})
userPtr := new(user)
strPtr := new(string)
fmt.Println(mpPtr, slicePtr, userPtr, strPtr)
}
// make仅用于 切片、集合、通道的初始化
func MakeCase() {
// 初始化切片,并设置长度和容量
slice := make([]int, 10, 20)
slice[0] = 10
//初始化集合,并设置集合的初始大小
mp := make(map[string]string, 10)
mp["A"] = "a"
// 初始化通道,设置通道的读写方向和缓冲大小
ch := make(chan int, 10)
ch1 := make(chan<- int, 10)
ch2 := make(<-chan int)
fmt.Println(slice, ch, ch1, ch2)
}
func SliceAndMapCase() {
// 定义切片
var slice []int
slice = []int{1, 2, 3, 4, 5}
slice1 := make([]int, 10)
slice1[1] = 10
fmt.Println(slice, slice1)
//切片的截取
slice2 := make([]int, 5, 10)
fmt.Println(len(slice2), cap(slice2))
slice2[0] = 0
slice2[1] = 1
slice2[2] = 2
slice2[3] = 3
slice2[4] = 4
// 切片截取
slice3 := slice2[1:10]
fmt.Println(len(slice3), cap(slice3), slice3)
//切片的附加
slice3 = append(slice3, 1, 2, 3, 4, 5)
fmt.Println(len(slice3), cap(slice3), slice3)
//集合,无序
mp := make(map[string]string, 10)
mp["A"] = "a"
mp["B"] = "b"
mp["C"] = "c"
mp["D"] = "d"
fmt.Println(mp)
// 无序
for k, v := range mp {
println(k, v)
}
// 删除集合元素
delete(mp, "B")
fmt.Println(mp)
}
类型转换
package _case
import (
"fmt"
"strconv"
"time"
"unsafe"
)
func ConvertCase() {
// 同一类数据类型转换 庶子和庶子、字符串和字符和字节
//不同类型的数据类型转换
// 接口类型转其他类型
// 数字类型的转换
var num1 int = 100
fmt.Println(int64(num1))
var num2 int64 = 100
fmt.Println(int(num2))
// 字符串与数字类型转换
var num3 = 100
fmt.Println(strconv.Itoa(num3) + "abc")
var str1 = "100"
fmt.Println(strconv.Atoi(str1))
var num4 int64 = 1010
fmt.Println(strconv.FormatInt(num4, 10))
var str2 = "1010"
fmt.Println(strconv.ParseInt(str2, 10, 64))
// 字符串与[]byte 转换
var str3 = "今天天气很好"
bytes1 := []byte(str3)
fmt.Println(bytes1)
fmt.Println(string(bytes1))
// 字符串与rune转换
// rune 为int32类型
// 将字符串转换为rune切片,实际上下杭rune切片中存储了字符串的unicode码点
var rune1 = []rune(str3)
fmt.Println(rune1)
fmt.Println(string(rune1))
fmt.Println(string(rune1[3]))
fmt.Println([]int32(str3))
// 接口类型转具体类型
// 断言
var inf interface{} = 100
var infStruct interface{} = user{}
i, ok := inf.(int)
fmt.Println(ok, i)
u, ok := infStruct.(user)
fmt.Println(ok, u)
// 时间类型转字符串
var t time.Time
t = time.Now()
timeStr := t.Format("2006-01-02 15:04:05Z07:00")
fmt.Println(timeStr)
// 字符串转时间
t2, _ := time.Parse("2006-01-02 15:04:05Z07:00", timeStr)
fmt.Println(t2)
// uintptr
u1 := user{}
// unsafe.Pointer 是一个通用的指针类型,不能用于计算
uPtr := unsafe.Pointer(&u1)
namePtr := unsafe.Pointer(uintptr(uPtr) + unsafe.Offsetof(u1.Name))
*(*string)(namePtr) = "nick"
fmt.Println(namePtr)
fmt.Println(u1)
}
变量作用域与函数
package _case
import "errors"
// 形参
// 局部变量
// 全局变量
// 函数可以有多个形参和多个返回值,返回值也可以和形参一样拥有参数名称
func SumCase(a, b int) (sum int, err error) {
//user.Name = "nick"
//user.Age = 18
if a <= 0 && b <= 0 {
err = errors.New("两数相加不能同时小于0")
return
}
sum = a + b
return
}
func ReferenceCase(a int, b *int) {
a += 1
*b += 1
}
// 全局变量
var g int
var G int
// 变量作用域
func ScopeCase(a, b int) {
c := 100
g = a + b + c
G = g
}
type User struct {
Name string
Age uint
}
func NewUser(name string, age uint) *User {
return &User{name, age}
}
// 获取user name属性
func (u User) GetName() string {
return u.Name
}
// 获取user age属性
func (u User) GetAge() uint {
return u.Age
}
运算符
package _case
import "fmt"
/*
// 运算符优先级
1.一元运算符优先级最高,例如:++ -- ! ^
2.二元运算符优先级从高到低
* / % << >> & &^
+ - | ^
== != < <= > >=
&&
||
*/
// 算术运算符
func ArithmeticCase() {
var a = 21
var b = 10
var c int
c = a + b
fmt.Printf("a + b 的值为 %d \n", c)
c = a - b
fmt.Printf("a - b 的值为 %d \n", c)
c = a * b
fmt.Printf("a * b 的值为 %d \n", c)
c = a / b
fmt.Printf("a / b 的值为 %d \n", c)
c = a % b
fmt.Printf("a 对 b 的取余值为 %d \n", c)
a++
fmt.Printf("a++ 的值为 %d \n", a)
a--
fmt.Printf("a-- 的值为 %d \n", a)
}
// 关系运算
func RelationCase() {
var a = 21
var b = 10
fmt.Println("a == b", a == b)
fmt.Println("a > b", a > b)
fmt.Println("a < b", a < b)
fmt.Println("a >= b", a >= b)
fmt.Println("a <= b", a <= b)
fmt.Println("a != b", a != b)
}
// 逻辑运算
func LogicCase() {
var a = true
var b = false
fmt.Println("a && b", a && b)
fmt.Println("a || b", a || b)
fmt.Println("!(a && b)", !(a && b))
fmt.Println("!(a || b)", !(a || b))
}
// 位运算
func BitCase() {
var a uint8 = 60
var b uint8 = 13
var c uint8 = 0
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", b)
fmt.Printf("%08b\n", c)
c = a & b
fmt.Println("a & b:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", b)
fmt.Printf("%08b\n", c)
c = a | b
fmt.Println("a | b:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", b)
fmt.Printf("%08b\n", c)
c = a ^ b
fmt.Println("a ^ b:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", b)
fmt.Printf("%08b\n", c)
c = a << 2
fmt.Println("a << 2:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", c)
c = a >> 2
fmt.Println("a >> 2:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", c)
c = ^a
fmt.Println("^a:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", c)
c = a &^ b
fmt.Println("a &^ b:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", b)
fmt.Printf("%08b\n", c)
c = a & ^b
fmt.Println("a & ^b:")
fmt.Printf("%08b\n", a)
fmt.Printf("%08b\n", b)
fmt.Printf("%08b\n", c)
}
// 赋值运算
func AssignmentCase() {
var a = 21
var c int
c = a
fmt.Println("c = a, c的值为", c)
c += a
fmt.Println("c += a, c的值为:", c)
c -= a
fmt.Println("c -= a, c的值为:", c)
c *= a
fmt.Println("c *= a, c的值为:", c)
c /= a
fmt.Println("c /= a, c的值为:", c)
var b uint8 = 60
fmt.Printf("b 值为 %d, 二进制表示: %08b\n", b, b)
b <<= 2
fmt.Printf("b <<= 2,b 值为 %d, 二进制表示: %08b\n", b, b)
b >>= 2
fmt.Printf("b >>= 2,b 值为 %d, 二进制表示: %08b\n", b, b)
b &= 2
fmt.Printf("b &= 2,b 值为 %d, 二进制表示: %08b\n", b, b)
b ^= 2
fmt.Printf("b ^= 2,b 值为 %d, 二进制表示: %08b\n", b, b)
b |= 2
fmt.Printf("b |= 2,b 值为 %d, 二进制表示: %08b\n", b, b)
}
golang 流程控制
- if else
- for
- switch case
- goto
package _case
import "fmt"
func FlowControlCase() {
ifElseCase(0)
ifElseCase(1)
ifElseCase(2)
forCase()
switchCase("A", 1)
switchCase("C", "1")
gotoCase()
}
func ifElseCase(a int) {
if a == 0 {
fmt.Println("执行 if 语句块")
} else if a == 1 {
fmt.Println("执行else if 语句块")
} else {
fmt.Println("执行else语句块")
}
}
func forCase() {
for i := 0; i < 10; i++ {
if i == 5 {
continue
}
fmt.Println("位置1 执行for 语句块 i:", i)
}
var i int
var b = true
for b {
fmt.Println("位置2 执行 for 语句块 i:", i)
i++
if i >= 10 {
b = false
}
}
list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for index, data := range list {
fmt.Printf("位置3 执行 for 语句块 index:%d data:%d \n", index, data)
}
mp := map[string]string{"A": "a", "B": "b", "C": "c"}
for k, value := range mp {
fmt.Printf("位置3 执行 for 语句块 k:%d value:%d \n", k, value)
}
str := "今天天气很好"
for index, char := range str {
fmt.Printf("w位置5 执行 for 语句块 index:%d char:%d \n", index, string(char))
}
var j int
for {
fmt.Println("位置6 执行 for 语句块 j:", j)
j++
if j >= 10 {
break
}
}
for i := 0; i < 2; i++ {
for j := 0; j < 2; j++ {
fmt.Printf("位置7 执行 for 语句块 i:%d j:%d \n", i, j)
}
}
lab1:
for i := 0; i < 2; i++ {
for j := 0; j < 2; j++ {
fmt.Printf("位置8 执行 for 语句块 i:%d j:%d \n", i, j)
break lab1
}
}
}
func switchCase(alpha string, in interface{}) {
switch alpha {
case "A":
fmt.Println("执行 A 语句块")
case "B":
fmt.Println("执行 B 语句块")
case "C", "D":
fmt.Println("执行 C,D 语句块")
fallthrough
case "E":
fmt.Println("执行 E 语句块")
case "F":
fmt.Println("执行 F 语句块")
}
switch in.(type) {
case string:
fmt.Println(" In 输入为字符串")
case int:
fmt.Println("In 输入为int类型")
default:
fmt.Println("未能输入in的类型")
}
}
func gotoCase() {
var a = 0
lab1:
fmt.Println("goto 位置1")
for i := 0; i < 10; i++ {
a += i
if a == 0 {
a += 1
goto lab1
}
}
}
golang 泛型编程
什么是泛型
- 泛型及开发过程中编写适用于所有类型的末班,只有在具体使用的时候才能确定其真正的类型
泛型的作用域应用场景
- 增加代码的服用,从同类型的服用到不同类型的代码复用
- 应用于不同类型间代码复用的场景,即不同类型需要写相同的处理逻辑时,最适合用泛型
泛型的利弊
1.提高了代码复用率,提高了编程效率
2.不同类型间代码复用,使代码风格更加优雅
3.增加了编译器的负担,降低了编译效率
golang的泛型怎么使用
1.泛型函数
2.泛型类型
3.泛型接口
4.泛型结构体
5.泛型receiver
泛型限制
1.匿名结构体域匿名函数不支持泛型
2.不支持类型断言
3.不支持泛型方法,只能通过receiver来实现方法的泛型处理
4.~后的类型必须为基本类型,不能为接口类型
interface.go
package _case
import "fmt"
// 基本接口,可用与变量的定义
type ToString interface {
String() string
}
func (u user) String() string {
return fmt.Sprintf("ID: %d,Name: %s,Age:%d", u.ID, u.Name, u.Age)
}
func (addr address) String() string {
return fmt.Sprintf("ID: %d,Name: %s,Addr:%s", addr.ID, addr.Province, addr.City)
}
// 泛型接口
type GetKey[T comparable] interface {
any
Get() T
}
func (u user) Get() int64 {
return u.ID
}
func (addr address) Get() int {
return addr.ID
}
// 列表转集合
func listToMap[k comparable, T GetKey[k]](list []T) map[k]T {
mp := make(MapT[k, T], len(list))
for _, data := range list {
mp[data.Get()] = data
}
return mp
}
func InterfaceCase() {
userList := []GetKey[int64]{
user{ID: 1, Name: "nick", Age: 18},
user{ID: 2, Name: "king", Age: 19},
}
addrList := []GetKey[int]{
address{ID: 1, Province: "湖南", City: "长沙"},
address{ID: 1, Province: "湖南", City: "湘潭"},
}
userMp := listToMap[int64, GetKey[int64]](userList)
fmt.Println(userMp)
addrMp := listToMap[int, GetKey[int]](addrList)
fmt.Println(addrMp)
}
recever.go
package _case
import "fmt"
// 泛型结构体
type MyStruct[T interface{ *int | *string }] struct {
Name string
Data T
}
// 泛型 receiver
func (myStruct MyStruct[T]) GetData() T {
return myStruct.Data
}
func ReceiverCase() {
data := 18
myStruct := MyStruct[*int]{
Name: "nick",
Data: &data,
}
data1 := myStruct.GetData()
fmt.Println(*data1)
str := "abcdefg"
myStruct1 := MyStruct[*string]{
Name: "nick",
Data: &str,
}
str1 := myStruct1.GetData()
fmt.Println(*str1)
}
simple.go
package _case
import "fmt"
func SimpleCase() {
var a, b = 3, 4
var c, d float64 = 5, 6
fmt.Println("不适用泛型,数字比较", getMaxNumInt(a, b))
fmt.Println("不适用泛型,数字比较", getMinNumFloat(c, d))
// 由编译器推断输入的类型
fmt.Println("适用泛型,数字比较", getMaxNum(a, b))
// 显示指定传入的类型
fmt.Println("适用泛型,数字比较", getMaxNum[float64](c, d))
}
func getMaxNumInt(a, b int) int {
if a > b {
return a
}
return b
}
func getMinNumFloat(a, b float64) float64 {
if a > b {
return a
}
return b
}
func getMaxNum[T interface{ int | float64 }](a, b T) T {
if a > b {
return a
}
return b
}
type CusNumT interface {
// 支持 uint8 | int32 | float32 与int64及其衍生类型
// ~ 表示支持类型的衍生类型
// | 表取冰机
// 多行之间去交集
uint8 | int32 | float64 | ~int64
int32 | float64 | ~int64 | uint16
}
// MyInt64 为int64的衍生类型,是具有基础类型int64的新类型,与int64是不同类型
type MyInt64 int64
// MyInt32 为int32 的别名, 与int32是同一个类型
type MyInt32 = int32
func CusNumTCase() {
var a, b int32 = 3, 4
var a1, b1 MyInt32 = 3, 4
fmt.Println("自定义泛型,数字比较", getMaxCusNum(a, b))
fmt.Println("自定义泛型,数字比较", getMaxCusNum(a1, b1))
var c, d float64 = 5, 6
fmt.Println("自定义泛型,数字比较", getMaxCusNum(c, d))
var e, f int64 = 7, 8
var g, h MyInt64 = 7, 8
fmt.Println("自定义泛型,数字比较", getMaxCusNum(e, f))
fmt.Println("自定义泛型,数字比较", getMaxCusNum(g, h))
}
func getMaxCusNum[T CusNumT](a, b T) T {
if a > b {
return a
}
return b
}
// 内置类型
func BuiltInCase() {
var a, b string = "hello", "world"
fmt.Println("内置类型 comparable 泛型类型约束", getBuiltInComparable(a, b))
var c, d float64 = 100, 100
fmt.Println("内置 comparable 泛型类型约束", getBuiltInComparable(c, d))
var f = 100.123
printBuiltInAny(f)
printBuiltInAny(a)
}
func getBuiltInComparable[T comparable](a, b T) bool {
// comparable 类型,只支持 == != 两个操作
if a == b {
return true
}
return false
}
func printBuiltInAny[T any](a T) {
fmt.Println("内置 any 泛型类型约束", a)
}
type.go
package _case
import "fmt"
type user struct {
ID int64
Name string
Age int8
}
type address struct {
ID int
Province string
City string
}
// 集合转列表
func mapToList[k comparable, T any](mp map[k]T) []T {
list := make([]T, len(mp))
var i int
for _, data := range mp {
list[i] = data
i++
}
return list
}
func myPrintln[T any](ch chan T) {
for data := range ch {
fmt.Println(data)
}
}
func TTypeCase() {
userMp := make(map[int64]user, 0)
userMp[1] = user{ID: 1, Name: "nick", Age: 18}
userMp[2] = user{ID: 2, Name: "king", Age: 19}
userList := mapToList[int64, user](userMp)
ch := make(chan user)
go myPrintln(ch)
for _, u := range userList {
ch <- u
}
addrMp := make(map[int64]address, 0)
addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
addrMp[2] = address{ID: 2, Province: "湖南", City: "湘潭"}
addrList := mapToList[int64, address](addrMp)
ch1 := make(chan address)
go myPrintln(ch1)
for _, addr := range addrList {
ch1 <- addr
}
}
// 泛型切片的定义
type List[T any] []T
// 泛型集合的定义
// 声明两个泛型,分别为k, v
type MapT[k comparable, v any] map[k]v
// 泛型通道的定义
type Chan[T any] chan T
func TTypeCase1() {
userMp := make(MapT[int64, user], 0)
userMp[1] = user{ID: 1, Name: "nick", Age: 18}
userMp[2] = user{ID: 2, Name: "king", Age: 19}
var userList List[user]
userList = mapToList[int64, user](userMp)
ch := make(Chan[user])
go myPrintln(ch)
for _, u := range userList {
ch <- u
}
addrMp := make(MapT[int, address], 0)
addrMp[1] = address{ID: 1, Province: "湖南", City: "长沙"}
addrMp[2] = address{ID: 2, Province: "湖南", City: "湘潭"}
var addrList List[address]
addrList = mapToList[int, address](addrMp)
ch1 := make(Chan[address])
go myPrintln(ch1)
for _, addr := range addrList {
ch1 <- addr
}
}
main.go
package main
import (
_case "awesomeProject/generic-T/case"
"context"
"os"
"os/signal"
)
func main() {
_case.SimpleCase()
_case.CusNumTCase()
_case.BuiltInCase()
_case.TTypeCase()
_case.TTypeCase1()
_case.InterfaceCase()
_case.ReceiverCase()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
defer stop()
<-ctx.Done()
}
如何使用golang标准库
标准库参考地址
- https://golang.google.cn/pkg/
标准库说明
- archive/tar:实现对tar存档的访问
- archive/zip:支持读取和写入zip存档
- arena:提供了为Go值集合分配内存的能力,并可以一次安全地手动释放这些空间
- bufio:实现韩冲I/O。包装io.Reade或io.Writer对象,创建另一个对象
- builtin:提供了Go预定义标识符的文档
- bytes:实现字节片操作的函数
- compress/bzip2:实现了bzip2解压缩
- compress/flate:实现RFC 1951中描述的DEFLATE压缩数据格式
- compress/gzip:实现RFC 1952的规定,实现gzip格式压缩文件的读写
- compress/lzw:实现了Lempel-Ziv-Welch压缩数据格式
- compress/zlib:实现zlib格式压缩数据的读写,按RFC 1950 中所规定
- container/heap:实现heap.Interface的任何类型提供堆操作
- container/list:实现了一个双链接列表
- container/ring:实现了环形链表的操作
- context:(上下文)定义Context类型,它跨API边界和进程之间携带截止日期、取消信号和其他请求范围值
- crypto:手机常用的加密常熟
- crypto/aes:实施AES加密(以前称为Rijndael)
- crypto/boring:公开仅在使用Go+BoringCrypto构建时可用的函数
- crypto/cipher:实现了标准的分组密码模式,这些模式可以封装在低级分组密码实现中
- crypto/des:实现数据加密标砖(DES)和三重数据加密算法(TDEA)
- crypto/dsa:实现FIPS 186-3中定义的数字签名算法
- crypto/ecdh:在NIST曲线和Curve25519上实现椭圆曲线Diffie-Hellman
- crypto/ecdsa:实现了FIPS 186-4和SEC 1.2.0版中定义的椭圆曲线数字签名算法
- crypto/ed25519:实现Ed25519签名算法
- crypto/elliptic:在素数字字段上实现标准NIST P-224、P-256、P-384和P-521椭圆曲线
encoding.go
package _case
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
)
func EncodingCase() {
type user struct {
ID int64
Name string
Age uint8
}
u := user{ID: 1, Name: "nick", Age: 18}
// 序列化
bytes, err := json.Marshal(u)
fmt.Println(bytes, err)
u1 := user{}
// 反序列化
err = json.Unmarshal(bytes, &u1)
fmt.Println(u1, err)
// base64编解码
str := base64.StdEncoding.EncodeToString(bytes)
fmt.Println(str)
bytes1, err := base64.StdEncoding.DecodeString(str)
fmt.Println(bytes1, err)
// 16禁止编解码
str1 := hex.EncodeToString(bytes1)
fmt.Println(str1)
bytes2, err := hex.DecodeString(str1)
fmt.Println(bytes2, err)
}
errors.go
package _case
import (
"errors"
"fmt"
"log"
"time"
)
type cusError struct {
Code string
Msg string
Time time.Time
}
func (err cusError) Error() string {
return fmt.Sprintf("%s: %s: %s", err.Code, err.Msg, err.Time.Format("2006-01-02T15:04:05Z07:00"))
}
func getCusError(code, msg string) error {
return cusError{Code: code, Msg: msg, Time: time.Now()}
}
func ErrorsCase() {
err := errors.New("程序发生了错误")
fmt.Println(err)
var a, b = -1, -2
res, err := sum(a, b)
fmt.Println(res, err)
if err != nil {
log.Println(err)
cusError, ok := err.(cusError)
if ok {
fmt.Println("打印错误信息:", cusError.Code, cusError.Msg, cusError.Time)
}
}
}
func sum(a, b int) (int, error) {
if a < 0 && b < 0 {
return 0, getCusError("500", "两数求和不能同时小于0")
}
return a + b, nil
}
fmt.go
package _case
import (
"fmt"
"os"
)
func FmtCase() {
// 打印到标准输出
fmt.Println("今天天气很好")
// 格式化,并打印到输出
fmt.Printf("%s天气很好\n", "今天")
// 格式化
str := fmt.Sprintf("%s天气很好", "今天、明天")
// 输出io.writer
fmt.Fprint(os.Stderr, str)
}
func FmtCase1() {
type simple struct {
value int
}
a := simple{
value: 10,
}
// 通用占位符
fmt.Printf("默认格式的值: %v\n", a)
fmt.Printf("包含字段名的值: %+v\n", a)
fmt.Printf("go语法表示的值: %#v\n", a)
fmt.Printf("go语法表示的类型: %T\n", a)
fmt.Printf("输出字面上的百分号:%%10")
// 整数占位符
v1 := 10
v2 := 20170 //"今"字码点值
fmt.Printf("二进制:%b \n", v1)
fmt.Printf("Unicode 码点转字符:%c \n", v2)
fmt.Printf("十进制:%d \n", v1)
fmt.Printf("八进制:%o \n", v1)
fmt.Printf("0o为前缀的八进制:%O \n", v1)
fmt.Printf("用单引号将字符的值包起来:%q \n", v2)
fmt.Printf("十六进制:%x \n", v1)
fmt.Printf("十六进制大写:%X \n", v1)
fmt.Printf("Unicode格式:%U \n", v2)
// 宽度设置
fmt.Printf("%v 的二进制:%b; go语法表示二进制位:%#b; 指定二进制宽度为8,不足8位补0:%08b \n", v1, v1, v1, v1)
fmt.Printf("%v 的二进制:%x; 使用go语法表示为,指定十六进制宽度为8,不足8位补0:%08x \n", v1, v1, v1)
fmt.Printf("%v 的字符为:%c; 指定宽度5位,不足5位补空格:%5c \n", v2, v2, v2)
// 浮点数占位符
var f1 = 123.789
var f2 = 12345678910.78999
fmt.Printf("指数为二的幂的无小数科学技术法: %b \n", f1)
fmt.Printf("科学计数法:%e \n", f1)
fmt.Printf("科学计数法,大写:%E \n", f1)
fmt.Printf("有小数点而无指数,即常规的浮点数格式。默认宽度和精度:%f \n", f1)
fmt.Printf("宽度为9:%9f \n", f1)
fmt.Printf("默认宽度,精度保留两位小数:%.2f \n", f1)
fmt.Printf("宽度为9,精度保留两位小数:%9.2f \n", f1)
fmt.Printf("宽度为9,精度保留0位小数:%9.f \n", f1)
fmt.Printf("根据情况自动选%%e 或 %%f 来输出,以产生更紧凑的输出(末位无0):%g %g \n", f1, f2)
fmt.Printf("根据情况自动选%%E 或 %%f 来输出,以产生更紧凑的输出(末位无0):%G %G \n", f1, f2)
fmt.Printf("以十六进制方式表示:%x \n", f1)
fmt.Printf("以十六进制方式表示,大写:%X \n", f1)
// 字符串占位符
var str = "今天是个好日子"
fmt.Printf("描述一下今天:%s \n", str)
// 用双引号将字符串包裹
fmt.Printf("描述一下今天:%q \n", str)
// 16进制表示
fmt.Printf("%x \n", str)
// 以空格作为两束之间的分隔符,并用大写16进制
fmt.Printf("% X \n", str)
// 指针占位符
var str1 = "今天是个好日子"
bytes := []byte(str1)
// 切片第0个元素的地址
fmt.Println(string(bytes))
mp := make(map[string]int, 0)
fmt.Printf("%p \n", mp)
var p *map[string]int = new(map[string]int)
fmt.Printf("%p \n", p)
}
log.go
package _case
import (
"log"
"os"
)
func init() {
log.SetFlags(log.Llongfile)
log.SetOutput(os.Stderr)
}
func LogCase() {
var a, b = -1, -2
_, err := sum(a, b)
if err != nil {
log.Println(err)
}
log.Printf("a: %d, b: %d,两数求和出现错误:%s \n", a, b, err.Error())
// fatal 日志会导致应用程序的退出
log.Fatalf("a: %d, b: %d,两数求和出现错误:%s \n", a, b, err.Error())
}
math.go
package _case
import (
"fmt"
"math"
)
func MathCase() {
fmt.Println("2的10次方:", math.Pow(2, 10))
fmt.Println("返回以2为底,1024的对数", math.Log2(1024))
fmt.Println("返回两数较大的值:", math.Max(2, 10))
fmt.Println("向上取整", math.Ceil(2.49))
fmt.Println("向下取整", math.Ceil(2.89))
fmt.Println("90度角的正弦值", math.Sin(math.Pi/2))
fmt.Println("1反正弦值", math.Asin(1))
}
reflect.go
package _case
import (
"errors"
"fmt"
"reflect"
)
func ReflectCase() {
type user struct {
ID int64
Name string
Hobby []string
}
type outUser struct {
ID int64
Name string
Hobby []string
}
u := user{ID: 1, Name: "nick", Hobby: []string{"篮球", "羽毛球"}}
out := outUser{}
res := copy(&out, u)
fmt.Println(res, out)
}
func copy(dest interface{}, source interface{}) error {
sType := reflect.TypeOf(source)
sValue := reflect.ValueOf(source)
// 如果为指针类型,则获取其值
if sType.Kind() != reflect.Ptr {
sType = sType.Elem()
sValue = sValue.Elem()
}
dType := reflect.TypeOf(dest)
dValue := reflect.ValueOf(dest)
if dType.Kind() != reflect.Ptr {
return errors.New("目标对象必须为struct指针类型")
}
dType = dType.Elem()
dValue = dValue.Elem()
if sValue.Kind() != reflect.Struct {
return errors.New("源对象必须为struct或struct的指针")
}
if dValue.Kind() != reflect.Struct {
return errors.New("目标对象必须为struct的指针")
}
destObj := reflect.New(dType)
for i := 0; i < dType.NumField(); i++ {
destField := dType.Field(i)
if sourceField, ok := sType.FieldByName(destField.Name); ok {
if destField.Type != sourceField.Type {
continue
}
value := sValue.FieldByName(destField.Name)
destObj.Elem().FieldByName(destField.Name).Set(value)
}
}
dValue.Set(destObj.Elem())
return nil
}
func SliceColumn(slice interface{}, column string) interface{} {
t := reflect.TypeOf(slice)
v := reflect.ValueOf(slice)
if t.Kind() == reflect.Ptr {
t = t.Elem()
v = v.Elem()
}
if v.Kind() != reflect.Struct {
val := v.FieldByName(column)
return val.Interface()
}
if v.Kind() != reflect.Slice {
return nil
}
t = t.Elem()
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
f, _ := t.FieldByName(column)
sliceType := reflect.SliceOf(f.Type)
s := reflect.MakeSlice(sliceType, 0, 0)
for i := 0; i < v.Len(); i++ {
o := v.Index(i)
if o.Kind() == reflect.Struct {
val := o.FieldByName(column)
s = reflect.Append(s, val)
}
if o.Kind() == reflect.Ptr {
v1 := o.Elem()
val := v1.FieldByName(column)
s = reflect.Append(s, val)
}
}
return s.Interface()
}
regexp.go
package _case
import (
"fmt"
"regexp"
)
func RegexpCase() {
// 构建一个正则表达式的对象
reg := regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
// 判断给定的字符串是否符合正则
fmt.Println(reg.MatchString("abcd[1234]"))
// 从给定的字符串查找符合条件的字符串
bytes := reg.FindAll([]byte("efg[456]"), -1)
fmt.Println(string(bytes[0]))
}
sort.go
package _case
import (
"fmt"
"sort"
)
type sortUser struct {
ID int64
Name string
Age uint8
}
type ByID []sortUser
// 获取长度
func (a ByID) Len() int {
return len(a)
}
// 交换位置
func (a ByID) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
// 返回i位置的ID是否小于j位置的ID
func (a ByID) Less(i, j int) bool {
return a[i].ID < a[j].ID
}
func SortCase() {
list := []sortUser{
{ID: 10, Name: "nick", Age: 18},
{ID: 11, Name: "nick", Age: 19},
{ID: 1, Name: "nick", Age: 17},
{ID: 9, Name: "nick", Age: 7},
{ID: 2, Name: "nick", Age: 27},
{ID: 12, Name: "nick", Age: 15},
{ID: 8, Name: "nick", Age: 30},
}
sort.Slice(list, func(i, j int) bool {
return list[i].Age < list[j].Age
})
fmt.Println(list)
// 实现sort.Interface接口
sort.Sort(ByID(list))
fmt.Println(list)
}
main.go
package main
import _case "awesomeProject/standard-lib/case"
func main() {
_case.EncodingCase()
_case.ErrorsCase()
_case.FmtCase()
_case.FmtCase1()
_case.LogCase()
_case.MathCase()
_case.ReflectCase()
_case.RegexpCase()
_case.SortCase()
}
Go特性
defer/recover 实现异常捕获和处理
应用场景
- 资源释放
- 异常捕获和处理
defer
- defer 关键词用来声明一个延迟调用函数,该函数可以是匿名函数也可以是具名函数
- defer 延迟函数执行时间(位置),方法return之后,返回参数到调用方法之前
- defer 延迟函数可以在方法返回之后改变函数的返回值
- 在方法结束(正常返回,异常结束)都会去调用defer声明的延迟函数,可以有效避免因异常的资源无法释放的问题
- 可以指定多个defer 延迟函数,多个演示函数执行顺序为后进先出
- defer 通常用于资源释放、异常捕获等场景,例如:关闭连接,关闭文件等
- 不建议在for循环中使用defer
recover
- Go语言的内奸函数,可以让进入宕机流程中 goroutine 恢复过来
- recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果
- 如果当前的 goroutine 出现public,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行
panic
- Go语言的一种异常机制
- 可通过 panic 函数主动抛出异常
package _case
import (
"fmt"
"io"
"log"
"os"
)
// defer 关键词用来声明一个延迟调用函数
// 该函数可以使匿名函数也可以是具名函数
// defer 延迟函数的执行顺序后进先出
func DeferCase1() {
fmt.Println("开始执行DeferCase1")
defer func() {
fmt.Println("调用匿名函数1")
}()
defer f1()
defer func() {
fmt.Println("调用匿名函数2")
}()
fmt.Println("DeferCase1执行结束")
}
// 参数预计算
func DeferCase2() {
i := 1
// 传参
defer func(j int) {
fmt.Println("defer j:", j)
}(i + 1)
// 闭包
defer func(j int) {
fmt.Println("defer i:", i)
}(i + 1)
i = 99
fmt.Println("i:", i)
}
func ExceptionCase() {
defer func() {
// 补货异常,恢复协程
err := recover()
// 异常处理
if err != nil {
fmt.Println("异常处理 defer recover", err)
}
}()
fmt.Println("开始执行ExceptionCase方法")
panic("ExceptionCase 抛出异常")
fmt.Println("ExceptionCase方法")
}
func FileReadCase() {
file, err := os.Open("./README.md")
if err != nil {
log.Fatal(err)
}
// 通过defer调用资源释放的方法
defer func() {
file.Close()
fmt.Println("释放文件资源")
}()
buf := make([]byte, 1024)
for {
n, err := file.Read(buf)
if err != nil && err != io.EOF {
log.Fatal(err)
}
if n == 0 {
break
}
fmt.Println(string(buf[:n]))
}
}
// 返回值
// defer 函数执行在return之后
func DeferCase3() {
i, j := f2()
fmt.Printf("i:%d,j:%d,g:%d\n", i, *j, g)
}
func f1() {
fmt.Println("调用了具名函数1")
}
var g = 100
func f2() (int, *int) {
defer func() {
g = 200
}()
fmt.Println("f2 g:", g)
return g, &g
}
interface 隐式实现
interface 隐式实现
- golang 对象实现interface 无需任何关键词,只需要该对象的方法集中包含接口定义的所有方法且方法签名一致
- 对象实现接口可以借助struct内嵌的特性,实现接口的默认实现
- 类型T方法集包含全部receiver T方法;类型*T方法集包含 receiver T + *T 方法
- 类型T实例 value或pointer可以调用全部的方法,编译器会自动转换
- 类型T实现接口,不管是T还是*T都实现了该接口
- 类型*T实现接口,只有T类型的指针实现了该接口
应用场景
- 面向接口编程
animal.go
package _case
import "fmt"
// 声明Animal接口
// 定义Animal行为
type Animal interface {
//吃
Each()
//喝
Drink()
//睡觉
Sleep()
//奔跑
Run()
}
type animal struct {
}
func (a animal) Each() {
fmt.Println("Animal Each 接口默认实现")
}
func (a *animal) Drink() {
fmt.Println("Animal Drink 接口默认实现")
}
func (a animal) Sleep() {
fmt.Println("Animal Sleep 接口默认实现")
}
func (a animal) Run() {
fmt.Println("Animal Run 接口默认实现")
}
func init() {
a := animal{}
a.Each()
a.Drink()
}
cat.go
package _case
import "fmt"
type Cat struct {
animal
}
func NewCat() Animal {
return &Cat{}
}
func (c *Cat) Each() {
fmt.Println("猫池老鼠")
}
dog.go
package _case
import "fmt"
type Dog struct {
animal
}
func NewDog() Animal {
return &Dog{}
}
func (a Dog) Each() {
fmt.Println("狗吃肉包子")
}
func (a Dog) Drink() {
fmt.Println("狗喝白开水")
}
func (a Dog) Sleep() {
fmt.Println("狗睡在窝里")
}
func (a Dog) Run() {
fmt.Println("狗屎四条腿跑")
}
dove.go
package _case
import "fmt"
type Dove struct {
}
func NewDove() Animal {
return &Dove{}
}
func (a *Dove) Each() {
fmt.Println("鸽子吃虫子")
}
func (a *Dove) Drink() {
fmt.Println("鸽子喝水")
}
func (a *Dove) Sleep() {
fmt.Println("鸽子睡在树上")
}
func (a *Dove) Run() {
fmt.Println("鸽子用翅膀飞")
}
main.go
package main
import _case "interface/case"
func main() {
cat := _case.NewCat()
animalLife(cat)
dog := _case.NewDog()
animalLife(dog)
dove := _case.NewDove()
animalLife(dove)
}
func animalLife(a _case.Animal) {
a.Each()
a.Drink()
a.Sleep()
a.Run()
}
通过通信共享内存
应用场景
- 协程间通信,即协程间数据传递
- 并发场景下利用channel的阻塞机制,作为同步机制(类似队列)
- 利用channel关闭时发送广播的特性,作为协程退出通知
channel 通过通讯共享内存
- channel的方向,读、写、读写
- channel 协程间通信信道
- channel 阻塞协程
- channel 并发场景下的同步机制
- channel 通知协程退出
- channel 的多路复用
注意
- channel 用于协程间通讯,必须存在读写双方,否则将造成死锁
channel.go
package _case
import (
"fmt"
"time"
)
// 协程间通信
func Communication() {
//定义一个可读可写的通道
ch := make(chan int, 0)
go communicationF1(ch)
go communicationF2(ch)
}
// F1 接收一个只写通道
func communicationF1(ch chan<- int) {
// 通过循环向通道写入 0~99
for i := 0; i < 100; i++ {
ch <- i
}
}
// F2 接收一个只读通道
func communicationF2(ch <-chan int) {
for i := range ch {
fmt.Println(i)
}
}
// 并发场景下,同步机制
func ConcurrentSync() {
// 带缓冲的通道
ch := make(chan int, 10)
// 向ch写入消息
go func() {
for i := 0; i < 100; i++ {
ch <- i
}
}()
// 向ch写入消息
go func() {
for i := 0; i < 100; i++ {
ch <- i
}
}()
// 从ch 读取消息并打印
go func() {
for i := range ch {
fmt.Println(i)
}
}()
}
// 通知协程退出与多路复用
func NoticeAndMultiplexing() {
ch := make(chan int, 0)
strCh := make(chan string, 0)
done := make(chan struct{}, 0)
go noticeAndMultiplexingF1(ch)
go noticeAndMultiplexingF2(strCh)
go noticeAndMultiplexingF3(ch, strCh, done)
time.Sleep(1 * time.Second)
close(done)
}
func noticeAndMultiplexingF1(ch chan<- int) {
for i := 0; i < 100; i++ {
ch <- i
}
}
func noticeAndMultiplexingF2(ch chan<- string) {
for i := 0; i < 100; i++ {
ch <- fmt.Sprintf("数字:%d", i)
}
}
// select 子句作为一个整体阻塞,其中任意channel准备就绪则继续执行
func noticeAndMultiplexingF3(ch <-chan int, strCh <-chan string, done chan struct{}) {
i := 0
for {
select {
case i := <-ch:
fmt.Println(i)
case str := <-strCh:
fmt.Println(str)
case <-done:
fmt.Println("收到退出通知,退出当前协程")
return
//default:
// fmt.Println("执行default语句")
}
i++
fmt.Println("累计执行次数: ", i)
}
}
main.go
package main
import (
_case "channel-select/case"
"os"
"os/signal"
)
func main() {
_case.Communication()
_case.ConcurrentSync()
_case.NoticeAndMultiplexing()
ch := make(chan os.Signal, 0)
signal.Notify(ch, os.Interrupt, os.Kill)
<-ch
}
函数式编程与闭包
函数概念
- 函数是程序中为了执行特定任务而存在的一系列执行代码,接收输入参数返回输出结果
- 头等函数(first class functions):支持头等函数的开发语言允许 将函数分配变量;作为参数传递给其他函数;作为其他函数的返回值。go支持头等函数
函数式编程的应用
- 闭包:函数内部引用了其他函数内部变量的函数,用于需要外部函数内部变量参数计划的场景
- 中间件:在不影响原有函数定义的前提下位函数增加额外的功能
- 函数类型对象:为函数扩展功能,且该功能需要该函数参与计算
闭包函数的陷阱
- for循环中使用闭包,可能会导致问题
func.go
package _case
import (
"errors"
"log"
)
func Sum(a, b int) (sum int, err error) {
if a <= 0 && b <= 0 {
err = errors.New("两数相加要求两数不能同时小于等于0")
return 0, err
}
sum = a + b
return sum, nil
}
// 将函数作为类型
type SumFunc func(a, b int) (int, error)
// LogMiddleware 将函数作为输入输出实现中间件
func LogMiddleware(in SumFunc) SumFunc {
// 返回的函数为闭包函数,其中in为闭包函数使用的外部函数内部变量
return func(a, b int) (int, error) {
log.Printf("日志中间件,记录操作数:a: %d, b: %d", a, b)
return in(a, b)
}
}
// 声明receiver为函数类型的方法,即函数类型的对象的方法
func (sum SumFunc) Accumulation(list ...int) (int, error) {
s := 0
var err error
for _, i := range list {
s, err = sum(s, i)
if err != nil {
return s, err
}
}
return s, err
}
closure.go
package _case
import (
"fmt"
"log"
)
func Fib(n int) int {
if n <= 2 {
log.Fatal("请选择大于2的位置")
}
t := tool()
var res int
for i := 1; i <= n-2; i++ {
res = t()
}
return res
}
// 斐波那契数列,X0+X1=X2.....求Xx的值
func tool() func() int {
var x0 = 0
var x1 = 1
var x2 = 0
return func() int {
x2 = x0 + x1
x0 = x1
x1 = x2
return x2
}
}
func ClosureTrap() {
for i := 0; i < 10; i++ {
go func() {
fmt.Println(i)
}()
}
}
main.go
package main
import (
"context"
"fmt"
_case "function/case"
"os"
"os/signal"
)
func main() {
// 调用函数
fmt.Println(_case.Sum(0, 0))
// 将函数赋值变量
f1 := _case.Sum
// 执行函数
fmt.Println(f1(1, 2))
// 将函数作为输入输出实现中间件
f2 := _case.LogMiddleware(_case.Sum)
// 再次附加中间
f2 = _case.LogMiddleware(f2)
fmt.Println(f2(1, 2))
f3 := _case.SumFunc(f1)
fmt.Println(f3.Accumulation(1, 2, 3, 4))
fmt.Println(f3.Accumulation(1, 2, 3, 4, 5))
fmt.Println(_case.Fib(10))
// 闭包的陷阱
_case.ClosureTrap()
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
defer cancel()
<-ctx.Done()
}
golang 文件读写操作
应用场景
- 上传下载文件
- 大文件分片传输
- 文件移动
- 文件内容按行获取
文件读写
- 文件复制
- 一次性读取文件内容并写入新文件
- 分片读取文件内容分布写入新文件
- 文件按行读取
copy.go
package _case
import (
"io"
"log"
"os"
"path"
)
func CopyDirToDir() {
list := getFiles(sourceDir)
for _, f := range list {
_, name := path.Split(f)
destFileName := destDir + "copy/" + name
// 复制文件
CopyFile(f, destFileName)
}
}
func CopyFile(srcName, dstName string) (int64, error) {
src, err := os.Open(srcName)
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.OpenFile(dstName, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer dst.Close()
return io.Copy(dst, src)
}
file.go
package _case
import (
"io/ioutil"
"log"
"strings"
)
// 源文件
const sourceDir = "source-file/"
const destDir = "dest-file"
func getFiles(dir string) []string {
fs, err := ioutil.ReadDir(dir)
if err != nil {
log.Fatal(err)
}
list := make([]string, 0)
for _, f := range fs {
if f.IsDir() {
continue
}
fullName := strings.Trim(dir, "/") + "/" + f.Name()
list = append(list, fullName)
}
return list
}
normal.go
package _case
import (
"log"
"os"
"path"
)
func ReadWriteFiles() {
list := getFiles(sourceDir)
for _, f := range list {
bytes, err := os.ReadFile(f)
if err != nil {
log.Fatal(err)
}
_, name := path.Split(f)
destName := destDir + "normal/" + name
err = os.WriteFile(destName, bytes, 0644)
if err != nil {
log.Fatal(err)
}
}
}
one_side.go
package _case
import (
"io"
"log"
"os"
"path"
)
func OneSideReadWriteToDest() {
list := getFiles(sourceDir)
for _, l := range list {
_, name := path.Split(l)
destFileName := destDir + "one-side/" + name
//文件写入
OneSideReadWrite(l, destFileName)
}
}
func OneSideReadWrite(srcName, destName string) {
src, err := os.Open(srcName)
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.OpenFile(destName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer dst.Close()
buf := make([]byte, 1024)
for {
n, err := src.Read(buf)
if err != nil && err != io.EOF {
log.Fatal(err)
}
if n == 0 {
break
}
dst.Write(buf[:n])
}
}
readline.go
package _case
import (
"bufio"
"fmt"
"io"
"log"
"os"
"strings"
)
// 以 README 文件为读取对象
const README = "README.md"
// 一次读取文件
// 按行拆分并打印
// 适合小文读取
func Readline1() {
fileHandler, err := os.OpenFile(README, os.O_RDONLY, 0444)
if err != nil {
log.Fatal(err)
}
defer fileHandler.Close()
bytes, err := io.ReadAll(fileHandler)
if err != nil {
log.Fatal(err)
}
list := strings.Split(string(bytes), "\n")
for _, l := range list {
fmt.Println(l)
}
}
// Readline2 通过bufio 按行读取
// buffalo 通过对io模块的封装,提供了数据的缓冲功能,能一定程度上减少大数据块读写带来的开销
// 当发起读写操作时,会尝试从缓冲区读取数据,缓冲区没有数据后,才会从数据源获取
// 缓冲区大小默认为4K
func Readline2() {
fileHandler, err := os.OpenFile(README, os.O_RDONLY, 0444)
if err != nil {
log.Fatal(err)
}
defer fileHandler.Close()
reader := bufio.NewReader(fileHandler)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Println(line)
}
}
// Readline3 通过 scanner 按行读取
// 单行默认大小64k
func Readline3() {
fileHandler, err := os.OpenFile(README, os.O_RDONLY, 0444)
if err != nil {
log.Fatal(err)
}
defer fileHandler.Close()
scanner := bufio.NewScanner(fileHandler)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}
}
main.go
package main
import _case "file-rw/case"
func main() {
_case.CopyDirToDir()
_case.ReadWriteFiles()
_case.OneSideReadWriteToDest()
_case.Readline1()
_case.Readline2()
_case.Readline3()
}
sync.WaitGroup 与 sync.Cond
sync.WaitGroup
- 等待一组协程完成
- 工作原理:通过计数器来获取协程的完成情况
- 启动一个协程是计数器+1 协程退出时计数器-1
- 通过wait方法阻塞主协程,等待计数器清零后才能继续执行后续操作
sync.WaitGroup 应用场景
- 通过协程并执行一组任务,且任务全部完成后才能进行下一步操作的情况
- 例如:汽车的生成,所有零件可以并行生产,只能等待所有零件生成完成后,才能组装
sync.WaitGroup 陷阱
- 协程间传递时需要以指针的方式或闭包的方式引用 WaitGroup 对象。否则将会造成死锁
sync.Cond 作用
- 设置一组协程根据条件阻塞,可以根据不同的条件阻塞
- 可以根据条件环形相对应的协程
sync.Cond 应用场景
- 应用于已发多收的场景,即一组协程需要等待某一个协程完成一些前置准备的情况
sync.Cond 注意事项
- 被叫方必须持有锁
- 主叫方可以持有锁,但允许不持有
- 尽可能的减少无效唤醒