10、go工程化与标准库

一、用go mod管理工程

  • 初始化项目go mod init $module_name,$module_name和目录名可以不一样。上述命令会生成go.mod文件
module main

go 1.19

require gonum.org/v1/gonum v0.12.0

  • 包查找规则:Go依次从当前项目、GOROOT、GOPATH下寻找依赖包
    • 从当前go文件所在的目录逐级向上查找go.mod文件(假设go.mod位于目录mode_path下),里面定义了module_name,则引入包的路径为"module_name/包相对于mode_path的路径"
    • go标准库提供的包在GOROOT/src下
    • 第三方依赖包在GOPATH/pkg/mod下
  • 包管理命令
    • 从go1.7开始,go get只负责下载第三方依赖包,并把它加到go.mod文件里,由go install负责安装二进制文件
      • go get github.com/mailru/easyjson会在GOPATH/pkg/mod目录下生成github.com/mailru/easyjson目录
      • go install github.com/mailru/easyjson/easyjson会在GOPATH/bin下生成easyjson二进制可执行文件
    • go mod tidy通过扫描当前项目中的所有代码来添加未被记录的依赖至go.mod文件或从go.mod文件中删除不再被使用的依赖

二、包引入规则

  • 包的声明
    • go文件的第一行声明 package xxx
    • 在包声明的上面可写关于包的注释,包注释也可以专门写在doc.go里
    • 包名跟目录名可以不同
    • 同一个目录下,所有go文件的包名必须一致
  • 包的引用
    • 可以直接使用同目录下其他go文件里的变量、函数、结构体
    • 跨目录使用则需要变量前加入包名,并且引入包所在的目录
      • imoprt "go-course/package":go-course是model名,package是目录名
      • mypackage.Add():mypackage是包名,它对应的目录是package
    • 在import块里可以引用父目录,也可以引用子目录
    • 引用关系不能构成一个环
    • 在import的目录前面可以给包起一个别名
      • imoprt asd "go-course/package"
      • asd.Add()

三、init调用链

  • init函数
    • main函数是go程序的唯一入口,所以main函数只能存在一个;main函数必须位于main包中
    • 在main函数执行之前会先执行init()函数
    • 在一个目录,甚至一个go文件里,init()可以重复定义
    • 引入其他包时,相应包里的init()函数也会在main()函数之前被调用
  • 包引用而不用import _ "net/http/pprof"
    • 在目录前加一个_,代码里却没有显式地使用这个包里的函数或变量,实际上是想执行这个包里的init()函数

四、可见性

  • 可见性
    • 以小写字母开头命名的函数、变量、结构体只能在本包内访问
    • 以大写字母开头命名的函数、变量、结构体在其他包中也可以访问
    • 如果结构体名字以大写字母开头,而其成员变量、成员方法以小写字母开头,则这样的成员只能在本包内访问
  • internal包
    • Go中命名为internal的package,可以被平级目录和上一级目录所访问,更上层的目录不能访问
    • c目录(internal的上一级目录)及其子孙目录之间可以任意import,但a目录和b目录不能import internal及其下属的所有目录

在这里插入图片描述

五、标准库

1 - 时间函数

  • 时间属性
func time_pro() {
    
    
	now := time.Now()
	fmt.Println(now.Unix())      //秒 -> 时间戳 1677915658
	fmt.Println(now.UnixMilli()) //毫秒 -> 时间戳 1677915658844
	fmt.Println(now.UnixMicro()) //微秒 -> 时间戳 1677915658844888
	fmt.Println(now.UnixNano())  //纳秒 -> 时间戳 1677915658844888900

	fmt.Println(now.Weekday().String())
	fmt.Println(now.Hour())
	fmt.Println(now.Minute())
	fmt.Println(now.Second())
	fmt.Println(now.Month())
	fmt.Println(int(now.Month())) //3 -> type Month int
	fmt.Println(now.Year())
	fmt.Println(now.YearDay()) //当前年的第几天
	fmt.Println(now.Day())     //当前月的第几天
	fmt.Println(now.Date())    //2023 March 4

}
  • 时间运算
func time_calc() {
    
    
	begin := time.Now()

	for i := 1; i < 10000; i++ {
    
    
		fmt.Printf("")
	}
	fmt.Println("use some time")

	//时间差计算方式1:
	useTime1 := time.Since(begin)       //计算时间差,返回类型是time.Duration
	fmt.Println(useTime1.Seconds())     //0.000523
	fmt.Println(useTime1.Nanoseconds()) //523000
	fmt.Println("----------------------")

	//时间差计算方式2:
	end := time.Now()
	useTime2 := end.Sub(begin)          //计算时间差,返回类型是time.Duration
	fmt.Println(useTime2.Seconds())     //0.000523
	fmt.Println(useTime2.Nanoseconds()) //523000
	fmt.Println("----------------------")

	//获取相对时间
	dua := time.Duration(8 * time.Hour)
	end_dua := begin.Add(dua)
	fmt.Println(end_dua) //2023-03-04 23:53:38.4740063 +0800 CST m=+28800.002135201
}
  • 时间格式化
const (
	TIME_FMT  = "2006-01-02 15:04:05"
	TIME_FMT1 = "2006-01-02"
	TIME_FMT2 = "20060102"
)

func time_fmt() {
    
    
	begin := time.Now()
	fmt.Println(begin) //2023-03-04 16:01:19.7069136 +0800 CST m=+0.003180001
	//格式化必须使用 "2006-01-02 15:04:05" 此格式
	fmt.Println(begin.Format(TIME_FMT))  //2023-03-04 16:01:19
	fmt.Println(begin.Format(TIME_FMT1)) //2023-03-04
	fmt.Println(begin.Format(TIME_FMT2)) //20230304
}
  • 文本转时间
func time_parse() {
    
    
	//不建议直接使用Parse
	if t, err := time.Parse(TIME_FMT1, "2022-02-18"); err == nil {
    
    
		fmt.Println(t.Year())  //2022
		fmt.Println(t.Month()) //February
		fmt.Println(t.Day())   //18
	}

	//建议使用ParseInLocation
	loc, _ := time.LoadLocation("Asia/Shanghai")
	if t, err := time.ParseInLocation(TIME_FMT1, "2022-02-18", loc); err == nil {
    
    
		fmt.Println(t.Year())  //2022
		fmt.Println(t.Month()) //February
		fmt.Println(t.Day())   //18
	}
}
  • 周期执行time.NewTicker
func main() {
    
    
	tk := time.NewTicker(1 * time.Second)
	defer tk.Stop()
	for i := 0; i < 10; i++ {
    
    
		<-tk.C //阻塞1秒钟
		fmt.Printf("第[%d]次执行,[%d]\n", i, time.Now().Unix())
	}
	tk.Stop()
}
  • 定时执行time.NewTimer<-time.After
func main() {
    
    
	// tm := time.NewTimer(time.Duration(3 * time.Second))
	// type Duration int64 -> 可以不需要time.Duration,Nanosecond也是Duration类型
	// const (
	// 	Nanosecond  Duration = 1
	// 	Microsecond          = 1000 * Nanosecond
	// 	Millisecond          = 1000 * Microsecond
	// 	Second               = 1000 * Millisecond
	// 	Minute               = 60 * Second
	// 	Hour                 = 60 * Minute
	// )
	tm := time.NewTimer(3 * time.Second)
	defer tm.Stop()
	<-tm.C //阻塞3秒钟
	fmt.Println(time.Now().Unix())

	//或者用
	<-time.After(3 * time.Second) //阻塞3秒钟
	fmt.Println(time.Now().Unix())
}
  • 重复使用timerReset
func main() {
    
    
	tm := time.NewTimer(3 * time.Second)
	defer tm.Stop()
	<-tm.C //阻塞3秒钟
	fmt.Println(time.Now().Unix())

	for i := 0; i < 6; i++ {
    
    
		tm.Reset(1 * time.Second)//使用Reset可以多次使用Timer
		<-tm.C
		fmt.Println(time.Now().Unix())
	}
}

2 - 数学计算

  • 数学常量
func main() {
    
    
	fmt.Println(math.E)                 //自然对数的底,2.718281828459045
	fmt.Println(math.Pi)                //圆周率,3.141592653589793
	fmt.Println(math.MaxInt)            //9223372036854775807
	fmt.Println(uint64(math.MaxUint64)) //18446744073709551615
}
  • math.NaN()
func math_nan() float64 {
    
    
	if f, err := strconv.ParseFloat("12.56X8", 64); err == nil {
    
    
		return f
	} else {
    
    
		return math.NaN()
	}
}

func main() {
    
    
	fmt.Println(math.NaN()) //NaN
	rect := math_nan()
	if math.IsNaN(rect) {
    
    
		fmt.Println("出错了")
	}
}
  • 常用函数
func main() {
    
    
	//常用函数
	fmt.Println(math.Ceil(1.1))     //向上取整,2
	fmt.Println(math.Floor(1.9))    //向下取整,1。 math.Floor(-1.9)=-2
	fmt.Println(math.Trunc(1.9))    //取整数部分,1
	fmt.Println(math.Modf(2.5))     //返回整数部分和小数部分,2  0.5
	fmt.Println(math.Abs(-2.6))     //绝对值,2.6
	fmt.Println(math.Max(4, 8))     //取二者的较大者,8
	fmt.Println(math.Min(4, 8))     //取二者的较小者,4
	fmt.Println(math.Mod(6.5, 3.5)) //x-Trunc(x/y)*y结果的正负号和x相同,3
	fmt.Println(math.Sqrt(9))       //开平方,3
	fmt.Println(math.Cbrt(9))       //开三次方,2.08008

	//对数和指数
	fmt.Println(math.Log(5))     //默认底数为自然对数math.E,1.60943
	fmt.Println(math.Log1p(4))   //等价于Log(1+p),确保结果为正数,1.60943
	fmt.Println(math.Log10(100)) //以10为底数,取对数,2
	fmt.Println(math.Log2(8))    //以2为底数,取对数,3
	fmt.Println(math.Pow(3, 2))  //x^y,9
	fmt.Println(math.Pow10(2))   //10^x,100
	fmt.Println(math.Exp(2))     //e^x,7.389

	//三角函数

	fmt.Println(math.Sin(1))
	fmt.Println(math.Cos(1))
	fmt.Println(math.Tan(1))
	fmt.Println(math.Tanh(1))
}
  • 随机数生成
func math_rand() {
    
    
	rand.Seed(time.Now().UnixMilli()) //如果对两次运行没有一致性要求,可以不设seed随机种子
	fmt.Println(rand.Int())           //随机生成一个整数
	fmt.Println(rand.Float32())       //随机生成一个浮点数
	fmt.Println(rand.Intn(100))       //100以内的随机整数,[0,100)
	fmt.Println(rand.Perm(100))       //把[0,100)上的整数随机打乱

	arr := []int{
    
    1, 2, 3, 4, 5, 6, 7, 8, 9}
	rand.Shuffle(len(arr), func(i, j int) {
    
     //随机打乱一个给定的slice
		arr[i], arr[j] = arr[j], arr[i]
	})
	fmt.Println(arr)
}
  • 自定义rand
func math_rand() {
    
    
	//创建一个Rand
	source := rand.NewSource(time.Now().UnixMicro()) //seed相同的情况下,随机数生成器产生的数列是相同的
	rander := rand.New(source)
	for i := 0; i < 10; i++ {
    
    
		fmt.Printf("%d ", rander.Intn(100))
	}
	fmt.Println()
	source.Seed(time.Now().UnixMicro()) //必须重置一下Seed
	rander2 := rand.New(source)
	for i := 0; i < 10; i++ {
    
    
		fmt.Printf("%d ", rander2.Intn(100))
	}
}

3 - I/O操作

  • 格式化输出
输出格式 输出内容
%t 单词 true 或 false
%b 表示为二进制
%d 表示为十进制
%e (=%.6e)有 6 位小数部分的科学计数法,如 -1234.456e+78
%f (=%.6f)有 6 位小数部分,如 123.456123
%g 根据实际情况采用 %e 或 %f 格式(获得更简洁、准确的输出)
%s 直接输出字符串或者字节数组
%v 值的默认格式表示
%+v 类似 %v,但输出结构体时会添加字段名
%#v 值的 Go 语法表示
值的类型的 Go 语法表示
  • 标准输入
func std_io() {
    
    
	fmt.Println("please input two string")
	var s1 string
	fmt.Scan(&s1)
	var s2 string
	fmt.Scan(&s2)
	fmt.Printf("you input %s %s", s1, s2)
	var i int
	fmt.Scan(&i)
	fmt.Printf("input int is %d", i)
}
  • 读写文件:一般使用read_file2来读文件
func read_file1() {
    
    
	if file, err := os.Open("go.mod"); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		defer file.Close()
		var buffer strings.Builder
		for {
    
    
			bs := make([]byte, 1024)
			if n, err := file.Read(bs); err != nil {
    
    
				fmt.Println(err)
				if err == io.EOF {
    
     //读到文件末尾
					break
				}
			} else {
    
    
				fmt.Printf("从文件中读出了%d个字节\n", n)
				buffer.WriteString(string(bs))
			}
		}
		fmt.Println(buffer.String())
	}
}
func read_file2() {
    
    
	if file, err := os.Open("go.mod"); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		defer file.Close()
		reader := bufio.NewReader(file) //使用bufio效率更高
		var i int
		for {
    
    
			if line, err := reader.ReadString('\n'); err != nil {
    
    
				fmt.Println(err)
				if err == io.EOF {
    
    
					break
				}
			} else {
    
    
				fmt.Printf("第%d行\n", i)
				fmt.Print(line)
				// fmt.Println(strings.Trim(line, "\n"))
				i++
			}
		}
	}
}
  • 写文件
func write_file1() {
    
    
	if file, err := os.OpenFile("lala.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		defer file.Close()
		file.Write([]byte("hello\n"))
	}
}

func write_file2() {
    
    
	if file, err := os.OpenFile("lala.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		defer file.Close()
		writer := bufio.NewWriter(file) //使用bufio效率更高
		writer.WriteString("hello")     //只是把内容写到内存缓冲区
		writer.WriteString("\n")
		writer.Flush() //强行将缓冲区内容刷新到磁盘中
	}
}
  • 创建文件/目录
    • os.Create(name string)//创建文件
    • os.Mkdir(name string, perm fs.FileMode)//创建目录
    • os.MkdirAll(path string, perm fs.FileMode)//增强版Mkdir,沿途的目录不存在时会一并创建
    • os.Rename(oldpath string, newpath string)//给文件或目录重命名,还可以实现move的功能
    • os.Remove(name string)//删除文件或目录,目录不为空时才能删除成功
    • os.RemoveAll(path string)//增强版Remove,所有子目录会递归删除
func main() {
    
    
	os.Remove("lala.txt")
	os.MkdirAll("p1/p2", 0666)
	os.Create("p1/p2/lala.txt")
	os.RemoveAll("p1")
	os.Create("old.txt")
	os.Rename("old.txt", "new.txt")
}
  • 获取文件属性
func main() {
    
    
	file, _ := os.Open("go.mod")
	fmt.Println(file.Name()) //go.mod
	info, _ := file.Stat()
	fmt.Println(info.IsDir())   //false
	fmt.Println(info.ModTime()) //2023-03-02 15:30:15.2281889 +0800 CST
	fmt.Println(info.Mode())    //-rw-rw-rw-
	fmt.Println(info.Size())    //57
	fmt.Println(info.Name())    //go.mod
}
  • 遍历目录
func walk_dir(path string) error {
    
    
	if fileInfos, err := ioutil.ReadDir(path); err != nil {
    
    
		return err
	} else {
    
    
		for _, fileInfo := range fileInfos {
    
    
			fmt.Println(fileInfo.Name())
			if fileInfo.IsDir() {
    
    
				// if err := walk_dir(path + "/" + fileInfo.Name()); err != nil {
    
    
				//更通用的写法,根据不同的操作系统来拼接目录
				if err := walk_dir(filepath.Join(path, fileInfo.Name())); err != nil {
    
    
					return err
				}
			}
		}
	}
	return nil
}

func main() {
    
    
	walk_dir("D:/Program Files")
}
  • log日志
func logger() {
    
    
	if file, err := os.OpenFile("mage.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		defer file.Close()
		logWriter := log.New(file, "[BIZ_NAME]", log.Ldate|log.Lmicroseconds)
		logWriter.Println("hello")
		logWriter.Println("jack")
		logWriter.Println("tom")
	}
}

在这里插入图片描述

  • 系统命令
func sys_call() {
    
    
	cmd_path, err := exec.LookPath("netstat")
	if err != nil {
    
    
		fmt.Println("os不支持 netstat命令")
	} else {
    
    
		fmt.Println(cmd_path)
		cmd := exec.Command("netstat", "-s")
		output, err := cmd.Output()
		if err != nil {
    
    
			fmt.Println(err)
		} else {
    
    
			fmt.Print(string(output))
		}
	}

	//不需要接收执行返回
	cmd := exec.Command("calc")
	if err := cmd.Run(); err != nil {
    
    
		fmt.Println(err)
	}
}

4 - 编码

  • compress:compress包下实现了zlib、bzip、gip、lzw等压缩算法
func compress() {
    
    
	fin, _ := os.Open("go.mod")
	fout, _ := os.OpenFile("go.mod.zlib", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	writer := zlib.NewWriter(fout)
	defer fin.Close()
	defer fout.Close()
	for {
    
    
		bs := make([]byte, 10)
		n, err := fin.Read(bs)
		if err != nil {
    
    
			if err == io.EOF {
    
    
				break
			} else {
    
    
				fmt.Println(err)
			}
		} else {
    
    
			writer.Write(bs[:n])
		}
	}
	writer.Flush()

	//读压缩文件
	fin, _ = os.Open("go.mod.zlib")
	reader, _ := zlib.NewReader(fin)
	io.Copy(os.Stdout, reader)
	reader.Close()
	fin.Close()
}
  • json:json是go标准库里自带的序列化工具,使用了反射,效率比较低
func test_json() {
    
    
	user := User{
    
    "jack", 18}
	if bs, err := json.Marshal(user); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		fmt.Println(string(bs)) //{"Name":"jack","Age":18}
	}
}
  • easyjson
    • easyjson只针对预先定义好的json结构体对输入的json字符串进行纯字符串的截取,并将对应的json字段赋值给结构体
    • easyjson -all xxx.go 生成go文件中定义的结构体对应的解析
    • xxx.go所在的package不能是main

在这里插入图片描述

func test_easyjson() {
    
    
	user := stru.User{
    
    Name: "jack", Age: 18}
	if bs, err := easyjson.Marshal(user); err != nil {
    
    
		fmt.Println(err)
	} else {
    
    
		fmt.Println(string(bs)) //{"Name":"jack","Age":18}
	}
}
  • base64
    • base64经常在http环境下用来传输较长的信息
    • 任意byte数组都可以采用base64编码转为字符串,并且可以反解回byte数组
    • 编码和解码的方法是公开、确定的, base64不属于加密算法
func test_base64() {
    
    
	fin, _ := os.Open("C:/Users/nd/Desktop/Gopher.jpeg")
	bs := make([]byte, 1<<20)
	n, _ := fin.Read(bs)
	str := base64.StdEncoding.EncodeToString(bs[:n])
	fmt.Println(str)

	bbb, _ := base64.StdEncoding.DecodeString(str)
	fout, _ := os.OpenFile("pic.png", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	fout.Write(bbb)
	fout.Close()
}

猜你喜欢

转载自blog.csdn.net/qq23001186/article/details/129242061