golang学习笔记

kingcwt2023-06-13前端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 如果没有返回值 返回值类型什么不写都可以
# 4go语言中 函数不支持重载 // 重载就是当函数名相同 形参不同是可以正常运行的 如果不能就是不支持重载
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标准库实现了平台(操作系统)无关的编程接口

标准库阅读方法

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单纯打印日志 【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区别:

  1. make只能用来分配及初始化类型为slice,map,chan的数据,new可以分配任意类型的数据

  2. new分配返回的是指针,即类型*T,make返回引用,即T

  3. 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路径中去
Last Updated 10/16/2023, 7:06:22 AM
What do you think?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8