golang学习笔记
go语言学习笔记
数据类型
---
几进制问题
二进制转换十进制
十进制转换二进制
八进制转换十进制 | 十进制转换八进制
// 八进制转换十进制
假设:八进制是16
1 6
= 1*8^1 6*8^0
= 8 + 6
= 14
// 十进制转换八进制
14/8 余数
1 6
0 1
倒着读 = 16 所以14转八进制等于 16
同理二进制转十进制 和 十进制转二进制是一个道理
同理 八进制转十六进制
八进制 ⇒十进制 ⇒十六进制
一个字节(byte)表示8位;对于有符号整数类型,一个字节可以表示的范围是-128到127,共256个数。其中,最高位是符号位,0表示正数,1表示负数。
- 1字节有符号整数表数范围是多少?
// 一个字节表示8位 首位0表示正数 1表示负数
// 二进制数 => 01111111
// 转换位十进制 => 127 所以 最大是127
// -------- 最小表数范围是多少?
// 首位是1后面表示符号位 就是 二进制 => 10000000
// 减1 => 01111111
// 取反 => 10000000 =>得到一个正数 2^7 = 128
// 加符号 => -128
// 最终表示范围 ===> -128 ~ 127
- 溢出范围报错
package main
import "fmt"
func main() {
// 定义一个整数类型
// var num1 int8 = 23
var exceedNum int8 = 128
fmt.Println(exceedNum)
}
//error: ./playground.go:8:23: cannot use 128 (untyped int constant) as int8 value in variable declaration (overflows)
查看变量占用的字节数
// 查看变量占用的字节数
package main
import (
"fmt"
"unsafe"
)
func main() {
num := 23
fmt.Println(unsafe.Sizeof(num)) // 8
}
浮点数
package main
import (
"fmt"
)
func main() {
// 定义浮点类型的数据: 浮点数可以用十进制形式表示,也可以用科学计数法表示, e/E 都可以
var num1 float32 = 3.14
fmt.Println(num1) // 3.14
var num2 float32 = -3.14
fmt.Println(num2) // -3.14
var num3 float32 = 314e-2
fmt.Println(num3) // 3.14
var num4 float32 = 314e2
fmt.Println(num4) // 31400
var num5 float32 = 314e+2
fmt.Println(num5) // 31400
var num6 float64 = 314e+2
fmt.Println(num6) // 31400
var num7 float32 = 256.000000917
fmt.Println(num7) // 256
var num8 float64 = 256.000000917
fmt.Println(num8) // 256.000000917
// golang中默认的浮点类型微:float64
var num9 = 3.1415926
fmt.Printf("num9的数据类型是%T", num9) // num9的数据类型是float64%
}
运算符
取模 [%] 和 自增 /自减
package main
import (
"fmt"
)
func main() {
// % 取模 等价公式: a % b = a - a / b * b
fmt.Println(10 % 3) // 10%3 = 10 - 10/3*3 = 10 - 3*3 = 1
fmt.Println(-10 % 3) // -1
fmt.Println(10 % -3) // 1
fmt.Println(-10 % -3) // -1
fmt.Println(15 % 4) // 15-15/4*4 = 15-12*4 = 3
fmt.Println(19 % 3) // 19-19/3*3 = 19-6*3 = 1
// ++ --
var a int = 8
a++
fmt.Println(a) // 9
a--
fmt.Println(a) // 8
// go 语言中 ++ 和 -- 只能独立使用 不能参与到运算中去
// go 语言中 ++ 和 -- 只能写在变量的后面 不能写在变量的前面 --a ++a 是错误的
}
&|*
func main() {
// &
var age int = 18
fmt.Println("age对应的存储空间的地址为:", &age) // age对应的存储空间的地址为: 0x14000014090
// *
var ptr *int = &age
fmt.Println(ptr) // 0x14000014090
fmt.Println("ptr这个指针指向的具体数值为:", *ptr) // ptr这个指针指向的具体数值为: 18
}
if else | switch | for
func main() {
// if
count := 30
if count < 40 {
fmt.Println("count为true") // count为true
}
if num := 40; num < 50 {
fmt.Println("num为true") // num为true
}
// if else
var age = 40
if age < 30 {
fmt.Println("age不满30啊")
} else {
fmt.Println("age已满30啊") // age已满30啊
}
// switch
// ps: case后面不需要break
// ps: case 表达式可以跟多个值
var score int = 87
switch score / 10 {
case 10:
fmt.Println("a")
case 11, 12, 13:
fmt.Println("11,12,13")
default:
fmt.Println("d") // d
}
// for
sum := 0
for i := 1; i <= 5; i++ {
sum += i
}
fmt.Println(sum) // 15
}
函数
# 1 函数首字母大写 可以被本包和其他包调用
# 2 首字母小写 只能被本包调用
# 3 如果没有返回值 返回值类型什么不写都可以
# 4 在go语言中 函数不支持重载 // 重载就是当函数名相同 形参不同是可以正常运行的 如果不能就是不支持重载
package main
import "fmt"
// 自定义函数
func cal(num1 int, num2 int) int {
num := 0
num += num1
num += num2
return num
}
// 返回多个值
func multiple(num1 int, num2 int) (int, int) {
num := 0
num += num1
num += num2
result := num1 - num2
return num, result
}
// 可变参数 多个参数
func test(args ...int) {
length := len(args)
fmt.Println(args, args[1],length) // [1 2] 1 2
}
func main() {
sum := cal(20, 7)
fmt.Println(sum) // 27
a1, a2 := multiple(20, 7)
fmt.Println(a1, a2) // 27 13
// 如果有返回值不想接受 可以用_进行忽略
a3, _ := multiple(20, 7)
fmt.Println(a3) // 27
test(1,2)
}
函数引用地址改变
package main
import "fmt"
func test1(num1 *int) {
*num1 = 30 // 取到num引用地址对应的值 进行赋值操作
fmt.Println(num1, *num1) // 0x1400011a018 30
}
func main() {
num := 10
test1(&num) // 传进去的是num的引用地址
fmt.Println(num) // 30
}
函数也是一种数据类型
package main
import "fmt"
func test(num int) {
fmt.Println(num)
}
func test02(num1 int, num2 float32, testfunc func(int)) {
fmt.Println(num1, num2) // 1 3.14
testfunc(num1) // 1
}
func main() {
// golang中 函数也是一种数据类型 可以赋值给变量
t := test
fmt.Printf("t对应的类型:%T,test1对应的类型:%T \n", t, test) // t对应的类型:func(int),test1对应的类型:func(int)
t(10) // 10
test02(1, 3.14, test)
}
自定义数据类型 【别名】
package main
import "fmt"
func test(num1 int) {
fmt.Println(num1)
}
// 别名 用法1
type myFunc func(int)
func test1(num1 int, testfunc1 myFunc) {
testfunc1(num1) // 1
}
// 别名用法2 类型前定义具体的名称 返回的时候可以不写
func test2(num1 int, num2 int) (sum1 int, sub1 int) {
sum1 = num1 + num2
sub1 = num1 - num2
return
}
func main() {
type myInt int
var a1 myInt = 1
var a2 int = 2
// error: 不能将int类型的值给自定义类型的值
a2 = a1 // a2 = int(a1) // 可以通过int类型将自定义类型转换为int类型 就不会报错
fmt.Println(a2, a2) // ./playground.go:74:7: cannot use a1 (variable of type myInt) as int value in assignment
fmt.Println("-------------")
test1(1, test)
t1, t2 := test2(1, 2)
fmt.Println(t1, t2) // 3 -1
}
包
- 包的声明和所在的文件夹名称最好保持一致
defer+recover机制处理错误
- 可以捕获错误 不会影响下面代码的执行
package main
import "fmt"
func test() {
defer func() {
err := recover()
if err != nil {
fmt.Println("err = ", err) // err = runtime error: integer divide by zero
}
}()
num := 10
num2 := 0
result := num / num2
fmt.Println("result = ", result)
}
func main() {
test() // 可以捕获错误 不会影响下面代码的执行
fmt.Println("main()下面的代码...") // main()下面的代码...
}
自定义返回error信息
- 不会影响下面代码的执行
package main
import (
"errors"
"fmt"
)
func test() (err error) {
num := 10
num2 := 0
if num2 == 0 {
return errors.New("除数不能为0")
} else {
result := num / num2
fmt.Println("result = ", result)
return nil
}
}
func main() {
err := test()
if err != nil {
fmt.Println("err = ", err) // 除数不能为0
}
fmt.Println("main()下面的代码...") // main()下面的代码...
}
如果遇到错误不往下执行【builtin-panic】
package main
import (
"errors"
"fmt"
)
func test() (err error) {
num := 10
num2 := 0
if num2 == 0 {
return errors.New("除数不能为0")
} else {
result := num / num2
fmt.Println("result = ", result)
return nil
}
}
func main() {
err := test()
if err != nil {
fmt.Println("err = ", err) // 除数不能为0
panic(err) // 除数不能为0
//* return 也行啊
}
// ----下面代码不会执行------
fmt.Println("main()下面的代码...")
}
数组
遍历 for range
package main
import (
"fmt"
)
func main() {
var scores [5]int
for i := 0; i < len(scores); i++ {
fmt.Printf("请录入第%d学生的成绩:", i+1)
fmt.Scanln(&scores[i]) //
}
sum := 0
for i := 0; i < len(scores); i++ {
sum += scores[i]
}
avg := sum / len(scores)
fmt.Printf("总成绩为:%v,总成绩平均分为%v \n", sum, avg)
for i := 0; i < len(scores); i++ {
fmt.Printf("第%d学生的成绩为:%d\n", i+1, scores[i])
}
fmt.Println("--------------第二种遍历方式--------------------------")
for i, v := range scores {
fmt.Printf("第%d学生的成绩为:%d\n", i+1, v)
}
}
定义数组的方式
package main
import (
"fmt"
)
func main() {
// 第一种:
var arr1 [3]int = [3]int{1, 2, 3}
fmt.Println(arr1)
// 第二种:
var arr2 = [3]int{4, 5, 6}
fmt.Println(arr2)
// 第三种:
var arr3 = [...]int{7, 8, 9}
fmt.Println(arr3)
// 第四种:
var arr4 = [...]int{1: 10, 2: 20}
fmt.Println(arr4)
}
切片
package main
import (
"fmt"
)
func main() {
// 定义数组:
intarr := [...]int{3, 5, 6, 7}
// 从0开始 [0,3) 3,5,6
slice := intarr[0:3]
fmt.Println("intarr=", intarr) // intarr= [3 5 6 7]
fmt.Println("slice=", slice) // slice= [3 5 6]
fmt.Println("slice len=", len(slice)) // slice len= 3
// 容量
fmt.Println("slice cap=", cap(slice)) // slice cap= 4
fmt.Printf("数组中下标为0位置的地址:%p\n", &intarr[0]) // 数组中下标为0位置的地址:0x1400012e000
fmt.Printf("slice切片的下标为0的地址:%p\n", &slice[0]) // slice切片的下标为0的地址:0x1400012e000
slice[0] = 99
fmt.Println("intarr=", intarr) // intarr= [99 5 6 7]
fmt.Println("slice=", slice) // slice= [99 5 6]
}
make定义切片
package main
import (
"fmt"
)
func main() {
// make底层创建一个数组 对外不可见 不能直接操作 通过slice间接去访问各个元素
// 定义切片:make函数的三个参数:1切片类型 2切片长度 3切片容量
slice := make([]int, 5, 10)
fmt.Println(slice) // [0 0 0 0 0]
fmt.Println("切片的长度:", len(slice)) // 切片的长度: 5
fmt.Println("切片的容量:", cap(slice)) // 切片的容量: 10
slice[0] = 66
slice[1] = 99
fmt.Println(slice) // [66 99 0 0 0]
}
切片的遍历
package main
import (
"fmt"
)
func main() {
// 定义切片:make函数的三个参数:1切片类型 2切片长度 3切片容量
slice := make([]int, 5, 10)
slice[0] = 66
slice[1] = 99
slice[2] = 43
slice[3] = 11
slice[4] = 22
for i := 0; i < len(slice); i++ {
fmt.Printf("slice[%d]=%v \t", i, slice[i]) // slice[0]=66 slice[1]=99 slice[2]=43 slice[3]=11 slice[4]=22
}
fmt.Println("\n-------------------------")
for k, v := range slice {
fmt.Printf("slice[%d]=%v\t", k, v) // slice[0]=66 slice[1]=99 slice[2]=43 slice[3]=11 slice[4]=22
}
}
切片的拷贝和追加
package main
import (
"fmt"
)
func main() {
intarr := []int{3, 4, 5, 6}
slice := intarr[0:2]
fmt.Println(slice) // [3 4]
slice2 := slice[0:1]
fmt.Println(slice2) // [3]
slice3 := append(slice2, 99, 100) // append 相当于创建了一个新的数组 把原来的数组复制过来
fmt.Println(slice3) // [3 99 100]
// 底层原理:
// 1 创建新的数组 将老数组中的值复制过来 在新数组中追加新的值
// 2 slice3 指向的是一个新的数组
// 给slic2追加值:
slice2 = append(slice2, 88)
fmt.Println(slice2) // [3 88]
fmt.Println("\n-------------------")
// todo: 拷贝
a := []int{1, 2, 3, 4, 5}
b := make([]int, 10)
copy(b, a)
fmt.Println(b) // [1 2 3 4 5 0 0 0 0 0]
}
map的定义
package main
import "fmt"
func main() {
// 1 map使用一定要mack 才会分配空间
// 2 map存储是无序的 key不可以重复 重复后者会替换前者 value可以重复
//
a := make(map[int]string, 10) // 10是容量 可以省略
a[20230201] = "zhangsan"
a[20230611] = "kingcwt"
fmt.Println(a, "\n", a[20230611])
// 另一种定义方式
c := map[int]string{
20200101: "wangwu",
20200102: "zhaoliu",
}
c[20230611] = "kingcwt"
fmt.Println(c)
}
map的操作和遍历
package main
import "fmt"
func main() {
b := map[int]string{
20230101: "元旦",
20230211: "春节",
}
// add:
b[20230405] = "清明节"
// change:
b[20230211] = "春节2"
// delete:
delete(b, 20230405)
fmt.Println(b)
// find:
v, ok := b[20230211] // v是值 ok是boolean 找到返回true 否则false
fmt.Println(v, ok)
fmt.Println("-----------------------------------")
// 遍历:
a := map[int]string{
20220103: "zhangsan",
20220104: "lisi",
20220105: "wangwu",
}
for k, v := range a {
fmt.Printf("key:%d, value:%s\n", k, v)
// key:20220105, value:wangwu
// key:20220103, value:zhangsan
// key:20220104, value:lisi
}
fmt.Println("-----------------------------------")
d := make(map[string]map[int]string, 3)
d["北京"] = make(map[int]string, 3)
d["北京"][20220103] = "zhangsan"
d["北京"][20220104] = "lisi"
d["北京"][20220105] = "wangwu"
d["上海"] = make(map[int]string, 3)
d["上海"][20220103] = "zhangsan"
d["上海"][20220104] = "lisi"
d["上海"][20220105] = "wangwu"
d["广州"] = make(map[int]string, 3)
d["广州"][20220103] = "zhangsan"
d["广州"][20220104] = "lisi"
d["广州"][20220105] = "wangwu"
for k, v := range d {
fmt.Println(k)
for k1, v1 := range v {
fmt.Printf("学号:%d,姓名:%s\t", k1, v1)
}
fmt.Println()
}
// 北京
// 学号:20220103,姓名:zhangsan 学号:20220104,姓名:lisi 学号:20220105,姓名:wangwu
// 上海
// 学号:20220103,姓名:zhangsan 学号:20220104,姓名:lisi 学号:20220105,姓名:wangwu
// 广州
// 学号:20220103,姓名:zhangsan 学号:20220104,姓名:lisi 学号:20220105,姓名:wangwu
}
面向对象【结构体创建方式】
package main
import "fmt"
type Class struct {
Name string
Age int
Hobby string
}
func main() {
// 1. 结构体创建方式1
var n1 Class
fmt.Println(n1) // {0}
n1.Name = "张三"
n1.Age = 18
n1.Hobby = "打篮球"
fmt.Println(n1) // {张三 18 打篮球}
// 2. 结构体创建方式2
var t Class = Class{"李四", 31, "打游戏"}
fmt.Println(t)
// 3. 结构体创建方式3
var c1 *Class = new(Class)
fmt.Println(c1) // &{0}
(*c1).Name = "王五"
(*c1).Age = 20
(*c1).Hobby = "打乒乓球"
fmt.Println(*c1) // {王五 20 打乒乓球}
// * 为了符合程序猿的编程习惯,go语言中提供了简化写法
c1.Name = "汪苏泷"
c1.Age = 30
c1.Hobby = "唱歌"
fmt.Println(*c1)
// 4. 结构体创建方式4
var c2 *Class = &Class{"王传君", 30, "唱歌"}
fmt.Println(c2) // &{王传君 30 唱歌}
c2.Hobby = "打豆豆"
fmt.Println(*c2) // {王传君 30 打豆豆}
}
面相对象 【结构体之间的转换】
package main
import "fmt"
type Student struct {
age int
}
type Person struct {
age int
}
type Stu Student
func main() {
// 强制转换
var s Student = Student{10}
var p Person = Person{20}
fmt.Println(s) // {10}
s = Student(p)
fmt.Println(s) // {20}
fmt.Println(p) // {20}
fmt.Println("\n------------------------")
// todo: 结构体进行type重新定义后 相当于起别名 Golang认为是两个不同的类型 但是相互间可以强转
var s1 Stu = Stu{30}
var s2 Student = Student{40}
s1 = Stu(s2)
fmt.Println(s1) // {40}
fmt.Println(s2) // {40}
}
结构体-方法
方法是作用在指定的数据类型上,和指定的数据类型绑定,因此自定义类型,都可以有方法,而不仅仅是struct
方法的声明和调用格式:
声明:
type A struct {
Num int
}
func (a A) test(){
fmt.Println(a.Num)
}
调用:
var a A
a.test()
// 个人理解 其实就是js的原型链 A其实就是类 Num就是属性 完事test() 其实就是原型上的一个方法 可以访问A上面的属性
- (1) func (a A) test() 相当于A结构体有一个方法叫test
- (2) (a A) 体现方法test和结构体A绑定关系
代码层面:
package main
import "fmt"
type Person struct {
Name string
}
func (p Person) test() {
p.Name = "王武"
fmt.Println(p.Name) // 王武
}
func main() {
var p Person
p.Name = "张三"
p.test()
fmt.Println(p.Name) // 张三
}
// 方法和指定的类型进行绑定的。 p.test()
go mod 使用方法
初始化模块
go mod init <项目名称>
依赖关系处理,根据go.mod文件
go mod tidy
将依赖包复制到项目下的test目录
go mod test
显示依赖关系
go list -m all
显示详细依赖关系
go list -m -json all
下载依赖
go mod download [path@version]
下载包
go get -u 包
golang并发之协程
- s1
package main
import (
"fmt"
"time"
)
func showMsg(msg string) {
for i := 0; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100) // 睡 100ms
}
}
func main() {
showMsg("javascript")
showMsg("go")
}
// 打印:
msg: javascript
msg: javascript
msg: javascript
msg: javascript
msg: javascript
msg: go
msg: go
msg: go
msg: go
msg: go
- s2
package main
import (
"fmt"
"time"
)
func showMsg(msg string) {
for i := 0; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100) // 睡 100ms
}
}
func main() {
go showMsg("javascript") // 这里开启了一个协程
showMsg("go")
}
// 打印:
msg: go
msg: javascript
msg: javascript
msg: go
msg: go
msg: javascript
msg: javascript
msg: go
msg: javascript
msg: go
- s3
package main
import (
"fmt"
"time"
)
func showMsg(msg string) {
for i := 0; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100) // 睡 100ms
}
}
func main() {
go showMsg("javascript")
go showMsg("go")
fmt.Println("main end") // 因为主函数退出 程序就结束了 所以协程会自动关闭 也就不会打印上面的
}
// 打印:
main end
- s4
package main
import (
"fmt"
"time"
)
func showMsg(msg string) {
for i := 0; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond * 100) // 睡 100ms
}
}
func main() {
go showMsg("javascript")
go showMsg("go")
time.Sleep(time.Millisecond * 2000) // 睡 2s
fmt.Println("main end")
}
// 打印:
msg: go
msg: javascript
msg: javascript
msg: go
msg: javascript
msg: go
msg: go
msg: javascript
msg: javascript
msg: go
main end
并发协程-demo
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
func responseSize(url string) {
fmt.Println("Step1:", url)
response, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
fmt.Println("Step2:", url)
defer response.Body.Close()
fmt.Println("Step3:", url)
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println("Step4:", len(body))
}
func main() {
go responseSize("https://www.qq.com")
go responseSize("https://www.baidu.com")
go responseSize("https://www.jd.com")
time.Sleep(10 * time.Second)
// 打印:
Step1: https://www.qq.com
Step1: https://www.jd.com
Step1: https://www.baidu.com
Step2: https://www.jd.com
Step3: https://www.jd.com
Step4: 186090
Step2: https://www.baidu.com
Step3: https://www.baidu.com
Step4: 227
Step2: https://www.qq.com
Step3: https://www.qq.com
Step4: 328
}
通道(chan)
package main
import (
"fmt"
"math/rand"
"time"
)
var values = make(chan int)
func send() {
rand.Seed(time.Now().UnixNano())
value := rand.Intn(10)
fmt.Printf("Sending value: %v\n", value)
time.Sleep(time.Second * 5)
values <- value
}
func main() {
defer close(values)
go send()
fmt.Println("wait...")
value := <-values
fmt.Printf("receive: %v\n", value)
fmt.Println("done")
}
// 打印:
wait...
Sending value: 3
receive: 3
done
waitGroup
- 等待所有group执行完成 在执行
package main
import (
"fmt"
"sync"
)
var wp sync.WaitGroup
func showMsg(i int) {
defer wp.Done() // wp.add(-1)的意思
fmt.Printf("i:%v\n", i)
}
func main() {
for i := 0; i < 10; i++ {
go showMsg(i)
wp.Add(1)
}
wp.Wait() // 等待所有的goroutine执行完毕
fmt.Println("main end")
}
// 打印:
i:9
i:5
i:6
i:7
i:8
i:2
i:3
i:1
i:4
i:0
main end
runtime.Goexit()
// package utils
func showGoexit() {
for i := 0; i < 10; i++ {
fmt.Printf("i:%v\n", i)
if i >= 5 {
runtime.Goexit() // 到5的时候 直接退出
}
}
}
func TestGoexit() {
go showGoexit()
time.Sleep(time.Second)
}
//-----
package main
import (
"go3/utils"
)
func main() {
utils.TestGoexit()
}
// 打印:
i:0
i:1
i:2
i:3
i:4
i:5
runtime.GOMAXPROCS()
func showA() {
for i := 0; i < 10; i++ {
fmt.Printf("A:%v\n", i)
time.Sleep(time.Microsecond * 100)
}
}
func showB() {
for i := 0; i < 10; i++ {
fmt.Printf("B:%v\n", i)
time.Sleep(time.Microsecond * 100)
}
}
func TestRuntimeGOMAXPROCS() {
runtime.GOMAXPROCS(1) // 设置cpu核心数为1
fmt.Printf("cpu num:%v\n", runtime.NumCPU()) // 打印当前机器的cpu核心数
go showA()
go showB()
time.Sleep(time.Second)
}
package main
import (
"go3/utils"
)
func main() {
utils.TestRuntimeGOMAXPROCS()
}
// 打印:
cpu num:12
B:0
A:0
A:1
B:1
B:2
A:2
A:3
B:3
B:4
A:4
A:5
B:5
B:6
A:6
A:7
B:7
B:8
A:8
A:9
B:9
goLang并发之mutex互斥锁实现同步
- 一个主线程里面 结果是100 正确的
var m int = 100
func add() {
m += 1
fmt.Println("add++", m)
}
func sub() {
m -= 1
fmt.Println("sub--", m)
}
func TestMutex() {
for i := 0; i < 100; i++ {
add()
sub()
}
fmt.Printf("end===:%v\n", m)
}
- 在开启多个线程之后 结果变为随机,即便前面等待线程执行完成 结果依然随机
var m int = 100
var wg sync.WaitGroup
func add() {
defer wg.Done()
m += 1
fmt.Println("add++", m)
}
func sub() {
defer wg.Done()
m -= 1
fmt.Println("sub--", m)
}
func TestMutex() {
for i := 0; i < 100; i++ {
wg.Add(1)
go add()
wg.Add(1)
go sub()
}
wg.Wait()
fmt.Printf("end===:%v\n", m)
}
- 通过互斥锁的方式 实现同步 最后结果都是100 正确
var m int = 100
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
lock.Lock()
defer wg.Done()
m += 1
fmt.Println("add++", m)
lock.Unlock()
}
func sub() {
lock.Lock()
defer wg.Done()
m -= 1
fmt.Println("sub--", m)
lock.Unlock()
}
func TestMutex() {
for i := 0; i < 100; i++ {
wg.Add(1)
go add()
wg.Add(1)
go sub()
}
wg.Wait()
fmt.Printf("end===:%v\n", m)
}
- 在开启多个线程之后 结果变为随机,即便前面等待线程执行完成 结果依然随机
var m int = 100
var wg sync.WaitGroup
func add() {
defer wg.Done()
m += 1
fmt.Println("add++", m)
}
func sub() {
defer wg.Done()
m -= 1
fmt.Println("sub--", m)
}
func TestMutex() {
for i := 0; i < 100; i++ {
wg.Add(1)
go add()
wg.Add(1)
go sub()
}
wg.Wait()
fmt.Printf("end===:%v\n", m)
}
- 通过互斥锁的方式 实现同步 最后结果都是100 正确
var m int = 100
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
lock.Lock()
defer wg.Done()
m += 1
fmt.Println("add++", m)
lock.Unlock()
}
func sub() {
lock.Lock()
defer wg.Done()
m -= 1
fmt.Println("sub--", m)
lock.Unlock()
}
func TestMutex() {
for i := 0; i < 100; i++ {
wg.Add(1)
go add()
wg.Add(1)
go sub()
}
wg.Wait()
fmt.Printf("end===:%v\n", m)
}
chan遍历
- for
package service
import "fmt"
var c = make(chan int)
func TestChan() {
go func() {
for i := 0; i < 2; i++ {
c <- i + 1
}
close(c) // 如果没有值 则关闭 读取默认值0
}()
r := <-c
fmt.Printf("r:%v\n", r)
r = <-c
fmt.Printf("r:%v\n", r)
r = <-c
fmt.Printf("r:%v\n", r)
}
//----------main-----
package main
import (
"go3/service"
)
func main() {
service.TestChan()
}
// 打印:
r:1
r:2
r:0
- for range
package service
import "fmt"
var c = make(chan int)
func TestChan() {
go func() {
for i := 0; i < 2; i++ {
c <- i + 1
}
close(c) // 如果没有值 则关闭 读取默认值0
}()
for v := range c {
fmt.Printf("v:%v\n", v)
}
}
select switch
1 select是Go中的一个控制结构,类似于switch 语句,用于处理异步IO操作. select会监听case语句中chan的读写操作,当case中chan读写操作为非阻塞状态(既能读写)时,将会触发相应的动作
select中的case语句必须是一个chan操作
select中的default子句总是可运行的
2 如果有多个case都可以运行,select会随机公平的选出一个执行,其他不会执行
3 如果没有可运行的case语句,且有default语句,那么就会执行default的动作
4 如果没有可运行的case语句,select将阻塞,直到某个case通信可以运行
package service
import (
"fmt"
"time"
)
var chanInt = make(chan int)
var chanStr = make(chan string)
func TestSelect() {
go func() {
chanInt <- 100
chanStr <- "hello"
defer close(chanInt)
defer close(chanStr)
}()
for {
select {
case r := <-chanInt:
fmt.Printf("chanInt:%v\n", r)
case r := <-chanStr:
fmt.Printf("chanStr:%v\n", r)
default:
fmt.Printf("default...\n")
}
time.Sleep(time.Second)
}
}
// ---main
package main
import (
"go3/service"
)
func main() {
service.TestSelect()
}
//打印:
default...
chanInt:100
chanStr:hello
chanStr:
chanStr:
chanInt:0
chanInt:0
chanStr:
chanStr:
chanInt:0
chanStr:
chanInt:0
chanInt:0
chanStr:
chanStr:
chanStr:
.........
golang并发编程之Timer
- Timer就是定时器的意思,可以实现一些定时操作,内部也是通过chan来实现的
package service
import (
"fmt"
"time"
)
func TestTimer() {
timer := time.NewTimer(time.Second * 2)
fmt.Printf("time.now:%v\n", time.Now())
t1 := <-timer.C //阻塞的,直到时间到了
fmt.Printf("t1:%v\n", t1)
}
//---main
package main
import (
"go3/service"
)
func main() {
service.TestTimer()
}
// 打印:
time.now:2023-06-26 23:11:01.129658 +0800 CST m=+0.000274584
t1:2023-06-26 23:11:03.130747 +0800 CST m=+2.001303126
// 只是单纯的等待 1
package service
import (
"fmt"
"time"
)
func TestTimer() {
fmt.Printf("time.now:%v\n", time.Now())
timer := time.NewTimer(time.Second * 2)
<-timer.C
fmt.Printf("time.now:%v\n", time.Now())
}
// 打印:
time.now:2023-06-26 23:16:18.480631 +0800 CST m=+0.000201834
time.now:2023-06-26 23:16:20.482047 +0800 CST m=+2.001557542
// 只是单纯的等待 2
time.Sleep(time.Second)
// 只是单纯的等待 3
<-time.After(time.Second*2)
timer := time.NewTimer(time.Second)
timer.Stop() // 停止定时器 执行下面代码
timer.Reset(time.Second *1) // 重新赋值为1秒
golang并发编程之Ticker
- Timer只执行一次,Ticker可以周期的执行
package service
import (
"fmt"
"time"
)
func TestTicker() {
ticker := time.NewTicker(time.Second)
for _ = range ticker.C {
fmt.Println("ticker...")
}
}
// main
package main
import (
"go3/service"
)
func main() {
service.TestTicker()
}
// 打印: 会一直打印
ticker...
ticker...
ticker...
ticker...
ticker...
ticker...
ticker...
ticker...
ticker...
ticker...
ticker...
...
// 停止周期打印
func TestTicker() {
ticker := time.NewTicker(time.Second)
counter := 1
for _ = range ticker.C {
fmt.Println("ticker...")
counter++
if counter >= 6 {
ticker.Stop()
break
}
}
}
// 打印:
ticker...
ticker...
ticker...
ticker...
ticker...
- ticker select
package service
import (
"fmt"
"time"
)
func TestTickerSelect() {
ticker := time.NewTicker(time.Second)
chanInt := make(chan int)
sum := 0
go func() {
for v := range chanInt {
fmt.Println(v)
sum++
}
}()
for _ = range ticker.C {
select {
case chanInt <- 1:
case chanInt <- 2:
case chanInt <- 3:
}
if sum >= 10 {
break
}
}
}
// main
package main
import (
"go3/service"
)
func main() {
service.TestTickerSelect()
}
// 打印:
3
1
2
2
1
1
3
2
2
3
原子变量
- atomic 提供的原子操作能够确保任一时刻只有一个goroutine对变量进行操作,善用atomic能够避免程序中出现大量的锁操作
- atomic 常见的操作有:
- 增减
- 载入read
- 比较并交换cas
- 交换
- 存储write
增减操作
atomic包中提供了如下以add为前缀的增减操作:
- func AddInt32(addr *int32, delta int32) (new int32)
- func AddInt64(addr *int64, delta int64) (new int64)
- func AddUint32(addr *uint32, delta uint32) (new uint32)
- func AddUint64(addr *uint64, delta unit64) (new uint64)
- func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
- 示例1
package service
import (
"fmt"
"sync/atomic"
)
// todo: 读写
func test_load_store() {
var i int32 = 100
atomic.LoadInt32(&i) // 读取 read
fmt.Printf("i:%v\n", i) // 100
atomic.StoreInt32(&i, 200) // 写入操作
fmt.Printf("i:%v\n", i) // 200
}
// todo: cas
func test_cas() {
// cas
var i int32 = 100
b := atomic.CompareAndSwapInt32(&i, 100, 200) // 比较并且修改 如果有别的线程进来修改这个值 哪就返回false
fmt.Printf("b: %v\n", b) // true
fmt.Printf("i: %v\n", i) // 200
}
func TestAtomicOperate() {
test_cas()
test_load_store()
}
- 示例2
// 方式1 通过lock的方式 保证输出的结果正确
package service
import (
"fmt"
"sync"
"time"
)
var i = 100
var lock sync.Mutex
func add() {
lock.Lock()
i++
lock.Unlock()
}
func sub() {
lock.Lock()
i--
lock.Unlock()
}
func TestAtomic() {
for i := 0; i < 100; i++ {
go add()
go sub()
}
time.Sleep(time.Second * 2)
fmt.Printf("i:%d\v", i)
}
// 打印:i:100
--------------------------------------------------
// 方式2 --原子操作
package service
import (
"fmt"
"sync/atomic"
"time"
)
var i int32 = 100
// cas compare and swap 比较并且交换
func add() {
atomic.AddInt32(&i, 1)
}
func sub() {
atomic.AddInt32(&i, -1)
}
func TestAtomic() {
for i := 0; i < 100; i++ {
go add()
go sub()
}
time.Sleep(time.Second * 2)
fmt.Printf("i:%d\v", i)
}
// 打印 i:100
//--------- main
package main
import (
"go3/service"
)
func main() {
service.TestAtomic()
}
golang标准库os模块-文件目录相关
os标准库实现了平台(操作系统)无关的编程接口
标准库阅读方法
- https://pkg.go.dev/std
- 去到本地go安装目录看源码
package mos
import (
"fmt"
"os"
)
// todo: 创建文件
func createFile() {
f, err := os.Create("a.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("f.name(): %v\n", f.Name())
}
}
// todo: 创建目录
func makeDir() {
err := os.Mkdir("a", os.ModePerm) //Mkdir 创建一个目录 os.ModePerm表示最高权限
if err != nil {
fmt.Printf("err: %v\n", err)
}
err2 := os.MkdirAll("a/b/c", os.ModePerm) // 创建a目录下的b目录下的c目录 a->b->c
if err2 != nil {
fmt.Printf("err2: %v\n", err2)
}
}
// todo: 删除目录或则文件
func remove() {
err := os.Remove("a.txt") // 删除a.txt文件
if err != nil {
fmt.Printf("err: %v\n", err)
}
err2 := os.RemoveAll("a") // 删除a下的所有文件
if err2 != nil {
fmt.Printf("err2: %v\n", err2)
}
}
// todo: 工作目录操作
func wd() {
dir, _ := os.Getwd() // 1. 获取工作目录
fmt.Printf("dir: %v\n", dir) // dir: /Users/cuihongran/Desktop/demo/go3
err := os.Chdir("d:/") // 2 修改工作目录
if err != nil {
fmt.Printf("err: %v\n", err) // chdir d:/: no such file or directory
}
dir2, _ := os.Getwd()
fmt.Printf("dir2: %v\n", dir2) // dir2: /Users/cuihongran/Desktop/demo/go3
s := os.TempDir() // 3 获取临时工作目录
fmt.Printf("s: %v\n", s) // s: /var/folders/qt/qfgjw21x1rl7zwyylqy453mc0000gn/T/
}
// todo: 重命名文件
func renameFile() {
err := os.Rename("a.txt", "testa.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
}
}
// todo: 读文件
func readFile() {
b, err := os.ReadFile("testa.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("b: %v\n", string(b[:]))
}
}
// todo: 写文件
func writeFile() {
s := "hello golang"
os.WriteFile("testa.txt", []byte(s), os.ModePerm)
}
// todo: 写入新内容
func readFile_writeFile() {
b, err := os.ReadFile("testa.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
oldContent := string(b[:])
newContent := "我是后写入的内容..."
mergeContent := oldContent + newContent
os.WriteFile("testa.txt", []byte(mergeContent), os.ModePerm)
}
}
func MosMain() {
createFile() // 在当前根目录下创建了a.txt
makeDir()
remove()
wd()
renameFile()
readFile()
writeFile()
readFile_writeFile()
}
// main
package main
import "go3/mos"
func main() {
mos.MosMain()
}
golang标准库os模块-file文件读操作
package mos
import (
"fmt"
"io"
"os"
)
// todo: 读取文件名称
func OpenClose() {
f, err := os.Open("testa.txt")
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("f.Name(): %v\n", f.Name())
f.Close()
}
}
// todo: 读取和创建文件
func OpenFileTxt() {
f, err := os.OpenFile("testa.txt", os.O_RDWR|os.O_CREATE, 755)
if err != nil {
fmt.Printf("err: %v\n", err)
} else {
fmt.Printf("f.Name(): %v\n", f.Name())
f.Close()
}
}
// todo: 读文件
func readOps() {
// * f.ReadAt(buf,3) 从第四个开始读
f, _ := os.Open("testa.txt")
for {
buf := make([]byte, 10)
n, err := f.Read(buf)
if err == io.EOF { // io.EOF 表示读取文件结束的错误
break
}
fmt.Printf("n: %v\n", n)
fmt.Printf("string(bft): %v\n", string(buf))
}
f.Close()
}
// todo: 读文件夹
func readDir() {
dir, _ := os.ReadDir("dao") // 读取的是dao下面的内容
for _, v := range dir {
fmt.Printf("v.IsDir(): %v\n", v.IsDir()) // 是文件夹 返回true 否则返回false
fmt.Printf("v.Name(): %v\n", v.Name()) // 名称
}
}
// todo: 随机读文件内容
func seekTxt() {
f, _ := os.Open("testa.txt")
f.Seek(3, 0) // 从第4个字开始读 读10个
buf := make([]byte, 10)
n, _ := f.Read(buf)
fmt.Printf("n: %v\n", n)
fmt.Printf("string(bft): %v\n", string(buf))
f.Close()
}
func TestReadMain() {
seekTxt()
}
package main
import "go3/mos"
func main() {
mos.TestReadMain()
}
golang标准库os模块-file文件写操作
package mos
import "os"
func write() {
// os.O_RDWR 读写权限
// os.O_APPEND 在原先内容的基础上追加内容
// os.O_TRUNC 覆盖
f, _ := os.OpenFile("test.txt", os.O_RDWR|os.O_TRUNC, 0777)
f.Write([]byte("hello javascript!"))
f.Close()
}
func writeString() {
// writeAt([]byte("aaaa",3)) // 从第四个字开始写入aaaa
// 以上等同于write()
// WriteString 其实就是 f.Write([]byte("hello javascript!"))
f, _ := os.OpenFile("test.txt", os.O_RDWR|os.O_TRUNC, 0777)
f.WriteString("hello typescript!")
f.Close()
}
func TestWriteMain() {
writeString()
}
package main
import "go3/mos"
func main() {
mos.TestWriteMain()
}
golang标准库os包进程相关操作
os.ProcAttr是一个结构体类型,用于描述和配置一个新进程的属性。它包含了启动一个新进程所需的各种属性和选项。
os.ProcAttr
结构体的定义如下:
type ProcAttr struct {
Dir string
Env []string
Files []*os.File
Sys *syscall.SysProcAttr
}
它包含了以下字段:
- Dir: 一个字符串,表示新进程的工作目录。如果为空字符串,则使用当前进程的工作目录。
- Env: 一个字符串切片,表示新进程的环境变量。每个元素都是以"key=value"的形式表示。
- Files: 一个
*os.File
切片,表示新进程的文件描述符。这可以用于重定向标准输入、输出和错误流。 - Sys: 一个指向
syscall.SysProcAttr
结构体的指针,表示新进程的系统级属性。这个字段通常用于更底层的进程控制和配置。 - 通过配置
os.ProcAttr
结构体的字段,你可以指定新进程的工作目录、环境变量以及文件描述符等属性。然后,可以使用os.StartProcess
函数来启动一个新进程,并传递该结构体作为参数。
package main
import (
"fmt"
"os"
)
func main() {
cmd := "/path/to/myprogram"
args := []string{"arg1", "arg2"}
attr := &os.ProcAttr{
Dir: "/path/to/myprogram/dir",
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
}
proc, err := os.StartProcess(cmd, args, attr)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("New process ID:", proc.Pid)
}
上面的示例演示了如何使用os.ProcAttr
结构体来启动一个新进程,并将标准输入、输出和错误流重定向到当前进程的对应流。
- demo
package mos
import (
"fmt"
"os"
"time"
)
func TestProcessMain() {
// 获取当前正在运行的进程id
fmt.Printf("os.Getpid(): %v\n", os.Getpid())
// 父id
fmt.Printf("os.Getppid(): %v\n", os.Getppid())
// 设置新进程的属性
attr := &os.ProcAttr{
// files指定新进程继承的活动文件对象
// 前三个分别为,标准输入,标准输出,标准错误输出
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
//新进程的环境变量
Env: os.Environ(),
}
cmd := "/bin/ls"
args := []string{"ls", "-l"}
// 开始一个新进程
p, err := os.StartProcess(cmd, args, attr)
if err != nil {
fmt.Printf("err:%v\n", err)
}
fmt.Println(p)
fmt.Println("进程ID:", p.Pid)
// 通过进程id查找进程
p2, _ := os.FindProcess(p.Pid)
fmt.Println("======", p2)
fmt.Printf("---------\n")
// 等待10秒,执行函数
time.AfterFunc(time.Second*10, func() {
// 向p进程发生退出信息
p.Signal(os.Kill)
})
// 等待进程p的退出,返回进程状态
ps, _ := p.Wait()
fmt.Println("ps:", ps.String())
}
package main
import "go3/mos"
func main() {
mos.TestProcessMain()
}
golang标准库io包 input output
Go语言中,为了方便开发者使用,将IO操作封装在了如下包中:
- io为IO原语(I/O primitives)提供基本的接口 os File Reader Writer
- io/ioutil封装一些实用的I/0函数
- fmt实现格式化I/O,类似c语言中的printf和scanf format fmt
- bufio 实现带缓冲I/0
package mio
import (
"fmt"
"io"
"log"
"os"
"strings"
)
func testNewReader() {
r := strings.NewReader("hello javascript")
buf := make([]byte, 10)
r.Read(buf)
fmt.Printf("string(buf): %v\n", string(buf))
}
func testCopy() {
r := strings.NewReader("hello world")
_, err := io.Copy(os.Stdout, r) //os.Stdout 表示在终端输出
if err != nil {
log.Fatal(err)
}
}
// 带缓冲的buffer去读取内容 这个更快
func testCopyBuffer() {
r1 := strings.NewReader("first reader hello javascript\n")
r2 := strings.NewReader("second reader hello golang\n")
buf := make([]byte, 8)
if _, err := io.CopyBuffer(os.Stdout, r1, buf); err != nil {
log.Fatal(err)
}
if _, err := io.CopyBuffer(os.Stdout, r2, buf); err != nil {
log.Fatal(err)
}
}
func testPipe() {
r, w := io.Pipe()
go func() {
fmt.Fprint(w, "some io.Reader stream to be read\n")
w.Close()
}()
if _, err := io.Copy(os.Stdout, r); err != nil {
log.Fatal(err)
}
}
func TestIoMain() {
//* io.MultiReader(r1,r2,r3) 一次读取多个
//* io.NewSectionReader(r,5,15) 读5-17
// testCopyBuffer()
testPipe()
}
package main
import (
"go3/mio"
)
func main() {
mio.TestIoMain()
}
golang标准库ioutil包
封装一些实用的I/O函数
- 一下有些过时 就不写demo了
名称 | 作用 |
---|---|
ReadAll | 读取数据,返回读到的字节slice |
ReadDir | 读取一个目录,返回目录入口数组[]os.Fileinfo |
ReadFile | 读取一个文件,返回文件内容(字节slice) |
WriteFile | 根据文件路径,写入字节slice |
TempDir | 在一个目录中创建指定前缀名的临时目录,返回新临时目录的路径 |
TempFile | 在一个目录中创建指定前缀名的临时文件,返回os.File |
golang标准库bufio
bufio
bufio包实现了有缓冲的I/0,它包装了一个io.Reader或io.Write接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象
常量
const(
defaultBufSize = 4096
)
type Reader
type Reader struct {
buf []byte
rd io.Reader
r,w int
err error
lastByte int
lastRuneSize int
}
Reader实现了给一个io.Reader接口对象附加缓冲
func NewReader
func NewReader(rd io.Reader) *Reader
func NewReaderSize
func NewRaderSize(rd, io.Raeader, size int) *Reader
newReaderSize创建一个具有最少有size尺寸的缓冲,从r读取的Reader,如果参数r已经是一个具有足够大缓冲的Reader类型值,会返回r
func(*Reader)Reset(r io.Reader)
func (b *Reader) Reset(r io.Reader)
Reset丢弃缓冲中的数据,清除任何错误,将b重设为其下层从r读取数据
func main(){
s := strings,NewRader("ABCEFG")
str := strings.NewReader("123456")
br := bufio.NewReader(s)
b,_ := br.ReadString('\n')
fmt.Println(b)
br.Reset(str)
b,_ = br.ReadString('\n')
fmt.Println(b)
}
package mio
import (
"bufio"
"fmt"
"os"
"strings"
)
func TestBufio() {
// r := strings.NewReader("hello world")
f, _ := os.Open("test.txt") // 把这个文件里的内容放到bufio Reader里面读取
r2 := bufio.NewReader(f) //bufio封装一下reader
s, _ := r2.ReadString('\n')
fmt.Printf("s: %v\n", s)
}
func TestReadSlice() {
s := strings.NewReader("ABC,DEF,GHI,JKL")
br := bufio.NewReader(s)
w, _ := br.ReadSlice(',')
fmt.Printf("w: %q\n", w) //w: "ABC,"
n, _ := br.ReadSlice(',')
fmt.Printf("n: %q\n", n) //n: "DEF,"
}
func TestReadBytes() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)
w, _ := br.ReadBytes(' ')
fmt.Printf("w: %q\n", w) //w: "ABC "
n, _ := br.ReadBytes(',')
fmt.Printf("n: %q\n", n) //n: "DEF GHI JKL"
// br.ReadString(' ') 方法同上
}
func TestWriteTo() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)
f, _ := os.OpenFile("test.txt", os.O_RDWR|os.O_APPEND, 0777)
defer f.Close()
br.WriteTo(f)
}
func TestBufioMain() {
// * r2.RaadByte() // 一个字节一个字节读
// * r2.UnreadByte() // 执行之后 会读上一个字节
// * r2.ReadLine() // 读一行 读到\n算一行
// w, isPrefix,_ = r2.ReadLine() // w 读取的内容 isPrefix是否是前缀(如果内容超过缓冲大小 代表是一个前缀 返回true 否则返回false)
TestWriteTo()
}
package main
import (
"go3/mio"
)
func main() {
mio.TestBufioMain()
}
golang标准库log
log简介
golang内置了log包,实现简单的日志服务.通过调用log包的函数,可以实现简单的日志打印功能
log使用
log包中有3个系列的日志打印函数,分别为print系列,panic系列,fatal系列
函数系列 | 作用 |
---|---|
单纯打印日志 【Print,Printf,Println】 | |
panic | 打印日志,抛出panic异常 |
fatal | 打印日志,强制结束程序(os.Exit(1)),defer函数不会执行 |
package mlog
import (
"fmt"
"log"
"os"
)
var logger *log.Logger
func testPrint() {
log.Print("My log")
log.Printf("my log: %v\n", 100)
name := "chr"
age := 27
log.Println(name, "cool", age)
}
func testPanic() {
defer fmt.Println("执行。。。。")
log.Print("my heart")
log.Panic("My panic")
fmt.Println("end ????") //这句不会执行 因为上面Panic执行抛出异常错误
}
func testFatal() {
defer fmt.Println("defer...")
log.Print("my log.")
log.Fatal("fatal...") //执行完直接退出了 os.Exit(1)
fmt.Println("end ?????") // 不会执行
// 2023/06/30 22:05:32 my log.
// 2023/06/30 22:05:32 fatal...
// exit status 1
}
func testInit1Func() {
// log.Ldate 日期
// log.Ltime 时间
// log.Lshortfile 文件代码哪行执行 ps: xxx.go:39
// log.Llongfile 显示路径
log.SetPrefix("[CHR GO:]") // 给输出的日志添加前缀 [CHR GO:] 2023/06/30 22:19:09 index.go:43: My log .... 【默认没前缀】
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
func testInit2Func() {
f, err := os.OpenFile("b.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0664)
// defer f.Close()
if err != nil {
log.Fatal("日志文件错误")
}
logger = log.New(f, "[日志前缀]:", log.Ldate|log.Ltime|log.Lshortfile)
}
func init() {
// testInit1Func()
testInit2Func()
}
func testLogOutput() {
// i := log.Flags()
// fmt.Printf("i:%v\n", i)
f, err := os.OpenFile("a.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0664)
defer f.Close()
if err != nil {
log.Fatal("日志文件错误")
}
log.SetOutput(f)
log.Print("我的日志---输出到文件。。。。")
}
func TestLogMain() {
// testLogOutput()
// testInit2Func()
logger.Print("我的日志输出到b.log中去。。。。")
}
package main
import (
"go3/mlog"
)
func main() {
mlog.TestLogMain()
}
golang标准库builtin
这个包提供了一些类型声明,变量和常量声明,还有一些便利函数,这个包不需要导入,这些变量和函数就可以直接使用
常用函数
append
func append(slice []type, elems ...type) []Type
slice = append(slice, elem1, elem2) //直接在slice后面添加单个元素,添加元素类型可以和slice相同,也可以不同
slice = append(slice, anotherSlice...) //直接将另外一个slice添加到slice后面,但其本质还是将antherSlice中的元素一个个添加到slice中,和第一种方式类似
new和make
new和make区别:
make只能用来分配及初始化类型为slice,map,chan的数据,new可以分配任意类型的数据
new分配返回的是指针,即类型*T,make返回引用,即T
new分配的空间被清零,make分配后,会进行初始化
package mbuiltin
import "fmt"
func testAppend() {
s := []int{1, 2, 3}
i := append(s, 100)
fmt.Printf("i: %v\n", i) // i: [1 2 3 100]
s1 := []int{4, 5, 6}
i2 := append(s, s1...)
fmt.Printf("i2: %v\n", i2) // i2: [1 2 3 4 5 6]
}
func testLen() {
s := "hello golang"
fmt.Printf("len(s): %v\n", len(s)) // len(s): 12
s1 := []int{1, 2, 3, 4, 5}
fmt.Printf("len(s1): %v\n", len(s1)) // len(s1): 5
}
func testPrintAndPrintln() {
name := "chr"
age := 27
print(name, " ", age, "\n") // chr 27
fmt.Println("------------------") // ------------------
println(name, " ", age) // chr 27 [自带换行]
}
func testPanic() {
defer fmt.Println("defer....") // defer....
panic("执行panic后 将抛出异常 下面代码不会执行 defer会执行") // panic: 执行panic后 将抛出异常 下面代码不会执行 defer会执行
fmt.Println("------------------") //这行代码编译器都会发暗 这行代码不会执行
}
func testNew() {
b := new(bool)
fmt.Printf("b: %T\n", b) // b: *bool
fmt.Printf("b: %v\n", *b) // b: false
i := new(int)
fmt.Printf("i: %T\n", i) // i: *int
fmt.Printf("i: %v\n", *i) // i: 0
s := new(string)
fmt.Printf("s: %T\n", s) // s: *string
fmt.Printf("s: %v\n", *s) // s:
}
func testMakeVsNew() {
var p *[]int = new([]int)
fmt.Printf("p: %v\n", p) // p: &[]
v := make([]int, 10)
fmt.Printf("v: %v\n", v) // v: [0 0 0 0 0 0 0 0 0 0]
}
func TestBuiltinMain() {
testMakeVsNew()
}
package main
import (
"go3/mbuiltin"
)
func main() {
mbuiltin.TestBuiltinMain()
}
bytes
package mbytes
import (
"bytes"
"fmt"
"strings"
)
func test1() {
var i int = 10
var b byte = 10
b = byte(i)
i = int(b)
fmt.Println(i, b)
var s string = "hello world"
b1 := []byte{1, 2, 3}
s = string(b1)
b1 = []byte(s)
fmt.Println(s, b1)
}
func testBytesContains() {
// bytes.Contains 判断b字节切片是否包含b1||b2的字节切片内容
s := "kingcwt"
b := []byte(s)
b1 := []byte("king")
b2 := []byte("chr")
b3 := bytes.Contains(b, b1) // true
fmt.Println(b3)
b4 := bytes.Contains(b, b2) // false
fmt.Println(b4)
c := strings.Contains("chr", "c")
fmt.Println(c)
}
func testBytesCount() {
// bytes.Count 判断s1,s2,s3在s中出现了几次
s := []byte("hellooooo")
s1 := []byte("h")
s2 := []byte("l")
s3 := []byte("o")
fmt.Println(bytes.Count(s, s1)) // 1
fmt.Println(bytes.Count(s, s2)) // 2
fmt.Println(bytes.Count(s, s3)) // 5
}
func testBytesRepeat() {
// bytes.Repeat b的内容复制1次,3次,10次
b := []byte("hi")
fmt.Println(string(bytes.Repeat(b, 1))) // hi
fmt.Println(string(bytes.Repeat(b, 3))) // hihihi
fmt.Println(string(bytes.Repeat(b, 10))) // hihihihihihihihihihi
}
func testBytesReplace() {
s := []byte("hello world omg")
old := []byte("o")
news := []byte("gg")
fmt.Println(string(bytes.Replace(s, old, news, 0))) // hello world omg 不替换
fmt.Println(string(bytes.Replace(s, old, news, 1))) // hellgg world omg 替换第一个
fmt.Println(string(bytes.Replace(s, old, news, 2))) // hellgg wggrld omg 替换前两个
fmt.Println(string(bytes.Replace(s, old, news, -1))) // hellgg wggrld ggmg 全部替换
}
func testBytesRunes() {
s := []byte("你好未来")
r := bytes.Runes(s)
fmt.Println("转换前的字符串长度:", len(s), s) // 转换前的字符串长度: 12 [228 189 160 229 165 189 230 156 170 230 157 165]
fmt.Println("转换后的字符串长度:", len(r)) // 转换后的字符串长度: 4
}
func testBytesJoin() {
s2 := [][]byte{[]byte("你好"), []byte("过去")}
s3 := []byte(",")
fmt.Println(string(bytes.Join(s2, s3))) // 你好,过去
s4 := []byte("#")
fmt.Println(string(bytes.Join(s2, s4))) // 你好#过去
}
func TestBytesMain() {
testBytesJoin()
}
package main
import (
"go3/mbytes"
)
func main() {
mbytes.TestBytesMain()
}
Reader
package main
import (
"bytes"
"fmt"
)
func testReader() {
data := "123456"
// 通过[]byte创建Reader
r := bytes.NewReader([]byte(data))
// 返回未读取部分的长度
fmt.Println("r len :", r.Len())
// 返回底层数据总长度
fmt.Println("r size :", r.Size())
fmt.Printf("-------\n")
buf := make([]byte, 2)
for {
// 读取数据
n, err := r.Read(buf)
if err != nil {
break
}
fmt.Println(string(buf[:n]))
}
fmt.Printf("-------------\n")
// 设置偏移量,因为上面的操作已经修改了读取位置等信息
r.Seek(0, 0)
for {
// 一个字节一个字节的读
b, err := r.ReadByte()
if err != nil {
break
}
fmt.Println(string(b))
}
fmt.Println("===========")
r.Seek(0, 0)
off := int64(0)
for {
// 指定偏移量
n, err := r.ReadAt(buf, off)
if err != nil {
break
}
off += int64(n)
fmt.Println(off, string(buf[:n]))
}
}
func main() {
testReader()
}
Buffer类型
缓冲区是具有读取和写入方法的可变大小的字节缓冲区,Buffer的零值是准备使用的空缓冲区
声明一个Buffer的四种方法:
var b bytes.Buffer // 直接定义一个Buffer变量,不用初始化,可以直接使用
b := new(bytes.Buffer) //// 使用New返回buffer变量
b :=bytes.NewBuffer(s []byte) // 从一个[]byte切片,构造一个Buffer
b :=bytes.NewBufferString(s string) // 从一个string变量,构造一个Buffer
往Buffer中写入数据
b.Write(d []byte) //将切片d写入Buffer尾部
b.WriteString(s string) //将字符串s写入Buffer尾部
b.WriteByte(c byte) //将字符串c写入Buffer尾部
b.WriteRunr(r rune) //将一个rune类型的数据放到缓冲器的尾部
b.WriteTo(w io.Writer) //将Buffer中的内容输出到实现了io.Writer接口的可写入对象中
- 将文件中的内容写入Buffer,则使用ReadForm(i io.Reader)
从Buffer中读取数据到指定容器
c := make([]byte,8)
b.Read(c) // 一次读取8个byte到c容器中,每次读取新的8个byte覆盖c中原来的内容
b.ReadByte() //读取第一个byte,b的第一个byte被拿掉,赋值给a=>a,_ := b.ReadByte()
b.ReadRune() //读取第一个rune,b的第一个rune被拿掉,赋值给r=>r,_ := b.ReadRune()
b.ReadBytes(delimiter byte) //需要一个byte作为分隔符,读的时候从缓冲器里找到第一个出现的分隔符
// (delim),找到后,把缓冲器头部开始到分隔符之前的所有byte进行返回,作为byte类型的slice,返回后,缓冲器也会空掉一部分
b.ReadString(delimiter byte)//需要一个byte作为分隔符,读的时候从缓冲器里找第一个出现的分隔符
//(delim),找到后,把从缓冲器头部开始到分隔符之前的所有byte进行返回,作为字符串返回,返回后,缓冲器也会空掉一部分
//b.ReadForm(i io.Reader)从一个实现io.Reader接口的r,把r里的内容读到缓冲器里,n返回读的数量
file,_ :=os.Open("text.txt")
buf :=bytes.NewBufferString("hello world")
buf.ReadFrom(file)
//将text.txt内容追加到缓冲器的尾部
fmt.Println(buf.String())
//清空数据
b.Reset()
//转换为字符串
b.String()
package mbuffer
import (
"bytes"
"fmt"
"io"
)
func testBuffer() {
var b bytes.Buffer
fmt.Printf("b : %v\n", b) // b : {[] 0 0}
b1 := bytes.NewBufferString("hello world")
fmt.Printf("b1 : %v\n", b1) // b1 : hello world
b2 := bytes.NewBuffer([]byte("hello"))
fmt.Printf("b2 : %v\n", b2) // b2 : hello
}
func testBuffer2() {
var b bytes.Buffer
n, _ := b.WriteString("hello chr")
fmt.Printf("n: %v\n", n) // n: 9
fmt.Printf("b: %v\n", string(b.Bytes())) // b: hello chr
}
func testBuffer3() {
b := bytes.NewBufferString("hello world")
b1 := make([]byte, 2)
for {
n, err := b.Read(b1)
if err == io.EOF {
break
}
fmt.Printf("n: %v\n", n) // n: 2
fmt.Printf("b1: %v\n", string(b1[0:n])) // b1: he
}
}
func TestBufferMain() {
testBuffer3()
}
golang标准库errors
error包实现了操作错误的函数,语言使用error类型来返回函数执行过程中遇到的错误,如果饭的error值为nil,则表示未遇到错误,否则error会返回一个字符串,用于说明遇到了什么错误
error结构
type error interface {
Error() string
}
error不一定表示一个错误,它可以表示任何信息,比如io包中就用error类型的io.EOF表示数据读取结束,而不是遇到了什么错误
errors包实现了一个最简单的error类型,只包含一个字符串,它可以记录大多数情况下遇到的错误信息,
errors包的用法也很简单,只有一个new函数,用于生成一个最简单的error对象:
func New(text string) error
package merrors
import (
"errors"
"fmt"
"time"
)
func testNewError(s string) (string, error) {
if s == "" {
err := errors.New("字符串不能为空")
return "", err
} else {
return s, nil
}
}
func testError() {
s, err := testNewError("")
if err != nil {
fmt.Printf("err: %v\n", err) // err: 字符串不能为空
}
fmt.Println(s)
}
type MyError struct {
When time.Time
What string
}
func (e MyError) Error() string {
return fmt.Sprintf("%v:%v", e.When, e.What)
}
func oops() error {
return MyError{
time.Date(2023, 3, 15, 22, 30, 0, 0, time.UTC),
"the file system has gone away",
}
}
func testCustomError() {
if err := oops(); err != nil {
fmt.Println(err)
}
}
func TestErrorsMain() {
testCustomError()
}
package main
import (
"go3/merrors"
)
func main() {
merrors.TestErrorsMain()
}
golang标准库中的sort包
sort包的内容,以及使用
sort包提供了排序切片和用户自定义数据集以及相关功能的函数
sort包主要针对[]int,[]float64,[]string,以及其他自定义切片的排序
结构体
type IntSlice struct
type Float64Slice
type StringSlice
package msort
import (
"fmt"
"sort"
)
func testSort() {
s := []int{1, 4, 3, 0, 9, 5}
sort.Ints(s)
fmt.Printf("s: %v\n", s) // s: [0 1 3 4 5 9]
}
type NewInts []uint
func (n NewInts) Len() int {
return len(n)
}
func (n NewInts) Less(i, j int) bool {
fmt.Println("Less:", i, j, n[i] < n[j], n)
return n[i] < n[j]
// Less: 1 0 false [1 3 2]
// Less: 2 1 true [1 3 2]
// Less: 1 0 false [1 2 3]
}
func (n NewInts) Swap(i, j int) {
n[i], n[j] = n[j], n[i]
}
func testSort2() {
n := []uint{1, 3, 2}
sort.Sort(NewInts(n))
fmt.Println(n)
}
func testStringSlice() {
ls := sort.StringSlice{
"100",
"10",
"80",
"60",
"90",
"66",
}
fmt.Println(ls) // [100 10 80 60 90 66]
sort.Strings(ls)
fmt.Println(ls) // [10 100 60 66 80 90]
fmt.Println("---------------------------")
ls1 := sort.StringSlice{
"d",
"a",
"ee",
"bd",
"va",
"co",
"gg",
"kk",
"rr",
"ww",
}
fmt.Println(ls1) // [d a ee bd va co gg kk rr ww]
sort.Strings(ls1)
fmt.Println(ls1) // [a bd co d ee gg kk rr va ww]
}
type testSlice [][]int
func (l testSlice) Len() int { return len(l) }
func (l testSlice) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l testSlice) Less(i, j int) bool { return l[i][1] < l[j][1] } // [i][1] 这里的1 表示第二个数排序 比方{1,4}取4和其他几项的第二个数比较 0就是第一个数,1就是第二个数
func testSlice2() {
fmt.Println("=============================")
ls := testSlice{
{1, 4},
{9, 3},
{6, 2},
}
fmt.Println(ls) // [[1 4] [9 3] [6 2]]
sort.Sort(ls)
fmt.Println(ls) // [[6 2] [9 3] [1 4]]
}
type testSliceMap []map[string]float64
func (l testSliceMap) Len() int { return len(l) }
func (l testSliceMap) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l testSliceMap) Less(i, j int) bool { return l[i]["a"] < l[j]["a"] }
func testSlice3() {
ls := testSliceMap{
{"a": 4, "b": 12},
{"a": 3, "b": 11},
{"a": 9, "b": 100},
}
fmt.Println(ls) // [map[a:4 b:12] map[a:3 b:11] map[a:9 b:100]]
sort.Sort(ls)
fmt.Println(ls) // [map[a:3 b:11] map[a:4 b:12] map[a:9 b:100]]
}
func TestSortMain() {
testSlice3()
}
package main
import (
"go3/msort"
)
func main() {
msort.TestSortMain()
}
time
time包提供测量和显示时间的功能
func test1()
package mtime
import (
"fmt"
"time"
)
func test1() {
now := time.Now() //获取当前时间
fmt.Printf("current time: %v\n", now)
year := now.Year()
month := now.Month()
day := now.Day()
hour := now.Hour()
minute := now.Minute()
secound := now.Second()
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, secound) // current time: 2023-07-02 20:44:52.937579 +0800 CST m=+0.000072251 2023-07-02 20:44:52
fmt.Printf("%T,%T,%T,%T,%T,%T,%T\n", now, year, month, day, hour, minute, secound) // time.Time,int,time.Month,int,int,int,int
timestamp := now.Unix() // 时间戳
// now.UnixNano() //纳秒级时间戳
timeObj := time.Unix(timestamp, 0) // 将时间戳转换为时间格式
fmt.Println("timeObj: ", timeObj) // timeObj: 2023-07-02 20:51:47 +0800 CST
y := timeObj.Year()
fmt.Println("y: ", y) // y: 2023
m := timeObj.Month() // m: July
fmt.Println("m: ", m)
d := timeObj.Day()
fmt.Println("d :", d) // d : 2
// now.Format() //格式化
}
func TestTimeMain() {
test1()
}
package main
import (
"go3/mtime"
)
func main() {
mtime.TestTimeMain()
}
时间格式化
时间类型有一个自带的方法Format进行格式化,需要主要的是Go语言中格式化时间模版不是常见的
y-m-d hⓂ️s 而是使用Go的诞生时间2006年1月2号15点04分( 2006 1 2 3 4)
package main
import (
"fmt"
"time"
)
func format() {
now := time.Now()
// 格式化的模版为Go的出生时间 2006年1月2号15点04分
// 24小时制
fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan")) // 2023-07-02 21:04:02.170 Sun Jul
//12小时制
fmt.Println(now.Format("2006-01-02 03:04:05.000 PM Mon Jan")) // 2023-07-02 09:04:02.170 PM Sun Jul
fmt.Println(now.Format("2006/01/02 15:04")) // 2023/07/02 21:04
fmt.Println(now.Format("15:04 2006/01/02")) // 21:04 2023/07/02
fmt.Println(now.Format("2006/01/02")) // 2023/07/02
}
func main() {
format()
}
解析字符串格式的时间
package main
import (
"fmt"
"time"
)
func format() {
now := time.Now()
// 格式化的模版为Go的出生时间 2006年1月2号15点04分
// 24小时制
fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan")) // 2023-07-02 21:04:02.170 Sun Jul
//12小时制
fmt.Println(now.Format("2006-01-02 03:04:05.000 PM Mon Jan")) // 2023-07-02 09:04:02.170 PM Sun Jul
fmt.Println(now.Format("2006/01/02 15:04")) // 2023/07/02 21:04
fmt.Println(now.Format("15:04 2006/01/02")) // 21:04 2023/07/02
fmt.Println(now.Format("2006/01/02")) // 2023/07/02
}
func main() {
now := time.Now()
fmt.Println(now)
// 加载时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
fmt.Println(err)
return
}
// 按照指定时区和指定格式解析字符串时间
timeObj, err := time.ParseInLocation("2006/01/02 15:04:05", "2018/07/25 13:15:11", loc)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(timeObj) // 2023-07-02 21:09:02.987422 +0800 CST m=+0.000077085 2018-07-25 13:15:11 +0800 CST
fmt.Println("=============")
fmt.Println(timeObj.Sub(now)) // -43279h54m24.078957s
}
encoding/json
这个包可以实现json的编码和解码,就是将json字符串转换为struct,或则将struct转换为json
核心的两个函数
func Marshal(v interface{}) ([]byte, error)
将struct编码成json,可以接收任意类型
func Unmarshal(data []byte, b interface{}) error
将json转码成struct结构体
两个核心结构体
type Decoder struct {
// contains filtered or unexported fields
}
从输入流读取并解析json
type Encoder struct {
// contains filtered or unexported fields
}
写入json到输出流
- demo
package mjson
import (
"encoding/json"
"fmt"
"log"
"os"
)
type Person struct {
Name string
Age int
Email string
}
func testToJson() {
p := Person{
Name: "chr",
Age: 27,
Email: "kingcwt@qq.com",
}
b, _ := json.Marshal(p)
fmt.Printf("b: %v\n", string(b)) // b: {"Name":"chr","Age":27,"Email":"kingcwt@qq.com"}
}
func testToStruct() {
b := []byte(`{"Name":"chr","Age":27,"Email":"kingcwt@qq.com"}`)
var p Person
json.Unmarshal(b, &p) // 转换 将b转换为p
fmt.Printf("p : %v\n", p) // {chr 27 kingcwt@qq.com}
}
func testToAny() {
b := []byte(`{"Name":"chr","Age":27,"Email":"kingcwt@qq.com","Person":["big tom"]}`)
var f interface{}
json.Unmarshal(b, &f)
fmt.Printf("f : %v\n", f) // f : map[Age:27 Email:kingcwt@qq.com Name:chr Person:[big tom]]
}
func testDecode() {
f, _ := os.Open("a.json")
defer f.Close()
d := json.NewDecoder(f)
var v map[string]interface{}
d.Decode(&v)
fmt.Printf("v: %v\n", v) // v: map[Age:27 Email:kingcwt@qq.com Name:chr]
fmt.Println("----------------------")
for k, v := range v {
fmt.Printf("k: %v\n", k)
fmt.Printf("v: %v\n", v)
}
//k: Name
//v: chr
//k: Age
//v: 27
//k: Email
//v: kingcwt@qq.com
}
func testEncode() {
type Person struct {
Name string
Age int
Email string
}
p := Person{
Name: "cwt",
Age: 18,
Email: "1003835955@qq.com",
}
f, err := os.OpenFile("a.json", os.O_WRONLY, 0777)
if err != nil {
log.Fatal(err)
}
defer f.Close()
e := json.NewEncoder(f)
e.Encode(p)
}
func TestJsonMain() {
testEncode()
}
package main
import "go3/mjson"
func main() {
mjson.TestJsonMain()
}
encoding/xml
xml包实现xml解析
核心的两个函数
func Marshal(v interface{}) ([]byte, error)
将struct编码成xml,可以接收任意类型
func Unmarshal(data []byte, v interface{}) error
将xml转码成struct结构体
package mxml
import (
"encoding/xml"
"fmt"
"io/ioutil"
"log"
"os"
)
func testXml() {
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}
person := Person{
Name: "cwt",
Age: 20,
Email: "100@qq.com",
}
b, err := xml.Marshal(person)
b1, err := xml.MarshalIndent(person, " ", " ") // " ":前缀没有就是空," ":两个空表示缩进
if err != nil {
log.Fatal(err)
}
fmt.Printf("b: %v\n", string(b)) // b: <person><name>cwt</name><age>20</age><email>100@qq.com</email></person>
fmt.Printf("b1: %v\n", string(b1))
// b1: <person>
// <name>cwt</name>
// <age>20</age>
// <email>100@qq.com</email>
// </person>
}
func testXmlUnmarshal() {
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}
s := `<person>
<name>cwt</name>
<age>20</age>
<email>100@qq.com</email>
</person>`
b := []byte(s)
var per Person
xml.Unmarshal(b, &per)
fmt.Printf("per: %v\n", per) // per: {{ person} cwt 20 100@qq.com}
}
func testXmlUnmarshalFile() {
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}
b, err := ioutil.ReadFile("a.xml")
if err != nil {
fmt.Printf("ERR: %v\n", err)
}
var per Person
xml.Unmarshal(b, &per)
fmt.Printf("per: %v\n", per) // per: {{ person} cwt 20 100@qq.com}
}
func testWrite() {
type Person struct {
XMLName xml.Name `xml:"person"`
Name string `xml:"name"`
Age int `xml:"age"`
Email string `xml:"email"`
}
person := Person{
Name: "cwt11",
Age: 18,
Email: "10022@qq.com",
}
f, _ := os.OpenFile("a.xml", os.O_WRONLY, 0777)
defer f.Close()
e := xml.NewEncoder(f)
e.Encode(person)
}
func TestXmlMain() {
testWrite()
}
package main
import (
"go3/mxml"
)
func main() {
mxml.TestXmlMain()
}
math
该包包含一些常量和一些有用的数学计算函数,例如: 三角函数,随机数,绝对值等
package mmath
import (
"fmt"
"math"
"math/rand"
)
func TestMathMain() {
fmt.Printf("math.MaxInt16: %v\n", math.MaxInt16) // math.MaxInt16: 32767
fmt.Printf("math.Pi: %v\n", math.Pi) // math.Pi: 3.141592653589793
fmt.Printf("rand.Int: %d\n", rand.Int) // rand.Int: 4296336224
a := rand.Intn(100)
fmt.Println("100以内:", a) // 100以内: 55
b := rand.Float32()
fmt.Println("小数:", b) // 小数: 0.87880206
}
package main
import (
"go3/mmath"
)
func main() {
mmath.TestMathMain()
}
相关的一些命令
- 模块找不到
go env // 查看当前环境变量配置
// GO111MODULE = off 强制Go 表现出 GOPATH 方式,即使在GOPATH 之外。
// GO111MODULE = auto 是默认模式
go env -w GO111MODULE=auto // 设置auto
go mod init 文件夹名称 // 把当前文件夹加入到GOPATH路径中去