云原生时代崛起的编程语言Go常用标准库实战

news2024/12/24 2:08:37

文章目录

  • 基础标准库
    • 简述
    • 字符串-string
      • 底层结构
      • 函数
      • 长度
      • 格式化输出
    • 模版-template
      • text/template
      • html/template
    • 正则表达式-regexp
    • 编码-encoding
      • Base64
      • JSON
      • XML
    • 时间-time
    • 网络-net
      • URL
      • HTTP客户端和服务端
    • 加密
    • IO操作
      • 读写文件
      • 环境变量
      • 命令行
    • 数据库
    • 排序-sort
    • 测试和基准测试

基础标准库

简述

Go语言的标准库覆盖网络、系统、加密、编码、图形等各个方面,可以直接使用标准库的 http 包进行 HTTP 协议的收发处理;网络库基于高性能的操作系统通信模型(Linux 的 epoll、Windows 的 IOCP);所有的加密、编码都内建支持,无需要再从第三方开发者处获取;Go 语言的编译器也是标准库的一部分,通过词法器扫描源码,使用语法树获得源码逻辑分支等;Go 语言的周边工具也是建立在这些标准库上。在标准库上可以完成几乎大部分的需求,Go 语言的标准库以包的方式提供支持,下表是 Go 语言标准库中常见的包及其功能。

Go语言标准库包名功 能
bufio带缓冲的 I/O 操作
bytes实现字节操作
container封装堆、列表和环形列表等容器
crypto加密算法
database数据库驱动和接口
debug各种调试文件格式访问及调试功能
encoding常见算法如 JSON、XML、Base64 等
flag命令行解析
fmt格式化操作
goGo 语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改
htmlHTML 转义及模板系统
image常见图形格式的访问及生成
io实现 I/O 原始访问接口及访问封装
log用于日志记录和控制台输出
math数学库
net网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等
os操作系统平台不依赖平台操作封装
path兼容各操作系统的路径操作实用函数
pluginGo 1.7 加入的插件系统。支持将代码编译为插件,按需加载
reflect语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值
regexp正则表达式封装
runtime运行时接口
sort排序接口
strings字符串转换、解析及实用函数
sync提供同步原语,如互斥锁和条件变量(上篇文章有专门讲解)
time时间接口
text文本模板及 Token 词法器

字符串-string

底层结构

标准库的strings包提供了许多有用的与字符串相关的函数。Go字符串底层的数据结构在runtime/strings.go中定义如下:

image-20230505092653839

从上面的stringStruct结构体得知其包含两个字段,一个是8个字节的万能指针,指向一个数组,数组里面存储就是实际的字符,另一个则是一个8个字节表示其长度,因此不管anyStrings多长通过unsafe.Sizeof(“anyStrings”)最终获取大小都是固定的16个字节。

image-20230505093428038

函数

下面是一些常见函数举例,可以到strings包文档中找到更多的函数

package main

import (
	"fmt"
	s "strings"
	"unsafe"
)

var p = fmt.Println

func main() {
	p(unsafe.Sizeof("anyStrings"))
	p(unsafe.Sizeof("anyStringsMoreThenLength"))
	p("Contains:  ", s.Contains("test", "es"))
	p("Count:     ", s.Count("test", "t"))
	p("HasPrefix: ", s.HasPrefix("test", "te"))
	p("HasSuffix: ", s.HasSuffix("test", "st"))
	p("Index:     ", s.Index("test", "e"))
	p("Join:      ", s.Join([]string{"a", "b"}, "-"))
	p("Repeat:    ", s.Repeat("a", 5))
	p("Replace:   ", s.Replace("foo", "o", "0", -1))
	p("Replace:   ", s.Replace("foo", "o", "0", 1))
	p("Split:     ", s.Split("a-b-c-d-e", "-"))
	p("ToLower:   ", s.ToLower("TEST"))
	p("ToUpper:   ", s.ToUpper("test"))
}

image-20230505093738945

长度

  • Go 语言的内建函数 len(),可以用来获取切片、字符串、通道(channel)等的长度,
  • Go 语言的字符串都以 UTF-8 格式保存,每个中文占用 3 个字节,因此使用 len() 获得两个中文文字对应的 6 个字节;
  • 针对ASCII 字符串长度使用 len() 函数,Unicode 字符串长度使用 utf8.RuneCountInString() 函数。如果没有使用 Unicode,汉字则显示为乱码。
  • ASCII 字符串遍历直接使用下标;Unicode 字符串遍历用 for range
  • bytes.Buffer实现字符串拼接。
package main

import (
	"bytes"
	"fmt"
	"unicode/utf8"
)

var p = fmt.Println

func main() {
	str1 := "Hello World!"
	str2 := "你好"
	fmt.Println(len(str1))                          // 12
	fmt.Println(len(str2))                          // 6
	fmt.Println(utf8.RuneCountInString(str2))       // 2
	fmt.Println(utf8.RuneCountInString("你好,world")) // 8

	// 声明字节缓冲
	var stringBuilder bytes.Buffer
	// 把字符串写入缓冲
	stringBuilder.WriteString(str1)
	stringBuilder.WriteString(str2)
	// 将缓冲以字符串形式输出
	fmt.Println(stringBuilder.String())

	theme := "狙击 start"
	for i := 0; i < len(theme); i++ {
		fmt.Printf("ascii: %c  %d\n", theme[i], theme[i])
	}

	for _, s := range theme {
		fmt.Printf("Unicode: %c  %d\n", s, s)
	}
}

image-20230505100058382

格式化输出

Go为传统的printf字符串格式化提供了很好的支持,Go提供了若干个打印“动词”,用于格式化一般Go值,下面是一些常见的字符串格式化任务的示例。

package main

import (
	"fmt"
	"os"
)

type point struct {
	x, y int
}

func main() {

	p := point{1, 2}
	fmt.Printf("struct1: %v\n", p)

	fmt.Printf("struct2: %+v\n", p)

	fmt.Printf("struct3: %#v\n", p)

	fmt.Printf("type: %T\n", p)

	fmt.Printf("bool: %t\n", true)

	fmt.Printf("int: %d\n", 123)

	fmt.Printf("bin: %b\n", 14)

	fmt.Printf("char: %c\n", 33)

	fmt.Printf("hex: %x\n", 456)

	fmt.Printf("float1: %f\n", 78.9)

	fmt.Printf("float2: %e\n", 123400000.0)
	fmt.Printf("float3: %E\n", 123400000.0)

	fmt.Printf("str1: %s\n", "\"string\"")

	fmt.Printf("str2: %q\n", "\"string\"")

	fmt.Printf("str3: %x\n", "hex this")

	fmt.Printf("pointer: %p\n", &p)

	fmt.Printf("width1: |%6d|%6d|\n", 12, 345)

	fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45)

	fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45)

	fmt.Printf("width4: |%6s|%6s|\n", "foo", "b")

	fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b")

	s := fmt.Sprintf("sprintf: a %s", "string")
	fmt.Println(s)

	fmt.Fprintf(os.Stderr, "io: an %s\n", "error")
}

image-20230505100238779

模版-template

text/template

Golang中的标准库template就像是一个“脚本语言解析器”,其中涉及到变量赋值、函数/方法调用和各种条件/循环控制结构等。template包实现了数据驱动的用于生成文本输出的模板,简单来说就是将一组文本嵌入另一组文本模版中,返回一个期望的文本。Go为模板操作提供了丰富的支持。嵌套模板,导入函数,表示变量,迭代数据等等都很简单。如果需要比CSV数据格式更复杂的东西,模板可能是一个不错的解决方案。模板的另一个应用是网站的页面渲染;当我们想要将服务器端数据呈现给客户端时,模板可以很好地满足要求。

Go提供了text/template和html/template这两个模板包,这两个包的部分函数看起来非常相似,实际功能也确实如此

package main

import (
	"os"
	"text/template"
)

type Inventory struct {
	Username string
	Phone    uint
	Tag      bool
	Sex      string
}

func main() {
	t1 := template.New("t1")
	t1, err := t1.Parse("Value is {{.}}\n")
	if err != nil {
		panic(err)
	}

	t1 = template.Must(t1.Parse("Value: {{.}}\n"))

	t1.Execute(os.Stdout, "some text")
	t1.Execute(os.Stdout, 5)
	t1.Execute(os.Stdout, []string{
		"Go",
		"Rust",
		"C++",
		"C#",
	})

	Create := func(name, t string) *template.Template {
		return template.Must(template.New(name).Parse(t))
	}

	t2 := Create("t2", "Name: {{.Name}}\n")

	t2.Execute(os.Stdout, struct {
		Name string
	}{"Jane Doe"})

	t2.Execute(os.Stdout, map[string]string{
		"Name": "Mickey Mouse",
	})

	t3 := Create("t3",
		"{{if . -}} yes {{else -}} no {{end}}\n")
	t3.Execute(os.Stdout, "not empty")
	t3.Execute(os.Stdout, "")

	t4 := Create("t4",
		"Range: {{range .}}{{.}} {{end}}\n")
	t4.Execute(os.Stdout,
		[]string{
			"Go",
			"Rust",
			"C++",
			"C#",
		})

	sweaters := Inventory{"移动", 10086, false, "难"}

	content := `{{.Phone}} of {{.Username}} {{if .Tag }} tag=true {{else}}   tag=false {{end}}`
	tmpl, err := template.New("test").Parse(content)
	//{{.Phone}}获取的是struct对象中的Phone字段的值
	if err != nil {
		panic(err)
	}
	err = tmpl.Execute(os.Stdout, sweaters) // 10086 of 移动  tag=true
	if err != nil {
		panic(err)
	}
}

image-20230505104701798

html/template

使用html/template来呈现网站,模板是纯文本,但变量和函数可以在大括号块内使用,模板包还提供了处理文件的便捷方法。html/template包是对text/template包的包装,因此能同于text/template基本都对html/template包同样适用,除了import语句无需其他任何修改。HTML模板提供了上下文感知安全性的额外好处,也可以防止诸如JavaScript注入之类的事情。如果要生成HTML格式的输出,参见html/template包,该包提供了和本包相同的接口,但会自动将输出转化为安全的HTML格式输出,可以抵抗一些网络攻击。

package main

import (
	"html/template"
	"net/http"
)

func tmpl(w http.ResponseWriter, r *http.Request) {
	t1, err := template.ParseFiles("test.html")
	if err != nil {
		panic(err)
	}
	t1.Execute(w, "hello world")
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}
	http.HandleFunc("/tmpl", tmpl)
	server.ListenAndServe()
}

创建test.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Go Web</title>
</head>
<body>
{{ . }}
</body>
</html>

访问测试地址http://localhost:8080/tmpl

image-20230505105330299

正则表达式-regexp

Go提供了对正则表达式的内置支持,其由regexp包实现了正则表达式搜索;Go标准库使用RE2语法,RE2语法也是Python、C和Perl使用的正则表达式语法,常见函数:

  • MatchString:regexp.MatchString()用来匹配子字符串。下面这个例子是检查字符串是否以Golang开头。我们使用^来匹配字符串中以文本的开始。我们使用^Golang作为正则表达式进行匹配。
  • Compile:Compile() 或者 MustCompile()创建一个编译好的正则表达式对象。假如正则表达式非法,那么Compile()方法会返回error,而MustCompile()编译非法正则表达式时不会返回error,而是会panic。如果你想要很好的性能,不要在使用的时候才调用Compile()临时进行编译,而是预先调用Compile()编译好正则表达式对象。
  • FindString:FindString()用来返回第一个匹配的结果。如果没有匹配的字符串,那么它会返回一个空的字符串,当然如果你的正则表达式就是要匹配空字符串的话,它也会返回空字符串。使用 FindStringIndex 或者 FindStringSubmatch可以区分这两种情况。
    • FindStringIndex:FindStringIndex()可以得到匹配的字符串在整体字符串中的索引位置。如果没有匹配的字符串,它会返回nil值。
    • FindStringSubmatch:FindStringSubmatch() 除了返回匹配的字符串外,还会返回子表达式的匹配项。如果没有匹配项,则返回nil值。
  • FindAllString:FindString方法的All版本,它返回所有匹配的字符串的slice。如果返回nil值代表没有匹配的字符串。
  • ReplaceAllString :用来替换所有匹配的字符串,返回一个源字符串的拷贝。

Go中与regexp相关的一些常见示例

package main

import (
	"bytes"
	"fmt"
	"regexp"
)

func main() {

	match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
	fmt.Println(match)

	r, _ := regexp.Compile("p([a-z]+)ch")

	fmt.Println(r.MatchString("peach"))

	fmt.Println(r.FindString("peach punch"))

	fmt.Println("idx:", r.FindStringIndex("peach punch"))

	fmt.Println(r.FindStringSubmatch("peach punch"))

	fmt.Println(r.FindStringSubmatchIndex("peach punch"))

	fmt.Println(r.FindAllString("peach punch pinch", -1))

	fmt.Println("all:", r.FindAllStringSubmatchIndex(
		"peach punch pinch", -1))

	fmt.Println(r.FindAllString("peach punch pinch", 2))

	fmt.Println(r.Match([]byte("peach")))

	r = regexp.MustCompile("p([a-z]+)ch")
	fmt.Println("regexp:", r)

	fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))

	in := []byte("a peach")
	out := r.ReplaceAllFunc(in, bytes.ToUpper)
	fmt.Println(string(out))
}

image-20230505112943707

编码-encoding

encoding 包是 Go 标准库中的一个重要包,主要用于数据编码和解码。encoding 包中包含了许多常用的数据编码和解码算法,如 JSON、XML、CSV、Base64 等,这些算法可以帮助我们将数据从一种格式转换为另一种格式,便于在不同的系统之间传输和处理。

Base64

Go提供对base64编码/解码的内置支持。

package main

import (
	b64 "encoding/base64"
	"fmt"
)

func main() {

	data := "abc123!?$*&()'-=@~"

	sEnc := b64.StdEncoding.EncodeToString([]byte(data))
	fmt.Println(sEnc)

	sDec, _ := b64.StdEncoding.DecodeString(sEnc)
	fmt.Println(string(sDec))
	fmt.Println()

	uEnc := b64.URLEncoding.EncodeToString([]byte(data))
	fmt.Println(uEnc)
	uDec, _ := b64.URLEncoding.DecodeString(uEnc)
	fmt.Println(string(uDec))
}

image-20230505151716877

JSON

Go提供了对JSON编码和解码的内置支持,包括来自内置和自定义数据类型的支持。需要使用encoding/json包进行json实现序列化与反序列化,go的json解析主要是编码和解码两个函数,序列化也就是由结构体转化为json string字符串,使用json.Marshal函数;反序列化就是将json string字符串转化为结构体,使用函数json.Unmarshal函数完成。

~~~go
package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type response1 struct {
	Page   int
	Fruits []string
}

type response2 struct {
	Page   int      `json:"page"`
	Fruits []string `json:"fruits"`
}

func main() {

	bolB, _ := json.Marshal(true)
	fmt.Println(string(bolB))

	intB, _ := json.Marshal(1)
	fmt.Println(string(intB))

	fltB, _ := json.Marshal(2.34)
	fmt.Println(string(fltB))

	strB, _ := json.Marshal("gopher")
	fmt.Println(string(strB))

	slcD := []string{"apple", "peach", "pear"}
	slcB, _ := json.Marshal(slcD)
	fmt.Println(string(slcB))

	mapD := map[string]int{"apple": 5, "lettuce": 7}
	mapB, _ := json.Marshal(mapD)
	fmt.Println(string(mapB))

	res1D := &response1{
		Page:   1,
		Fruits: []string{"apple", "peach", "pear"}}
	res1B, _ := json.Marshal(res1D)
	fmt.Println(string(res1B))

	res2D := &response2{
		Page:   1,
		Fruits: []string{"apple", "peach", "pear"}}
	res2B, _ := json.Marshal(res2D)
	fmt.Println(string(res2B))

	byt := []byte(`{"num":6.13,"strs":["a","b"]}`)

	var dat map[string]interface{}

	if err := json.Unmarshal(byt, &dat); err != nil {
		panic(err)
	}
	fmt.Println(dat)

	num := dat["num"].(float64)
	fmt.Println(num)

	strs := dat["strs"].([]interface{})
	str1 := strs[0].(string)
	fmt.Println(str1)

	str := `{"page": 1, "fruits": ["apple", "peach"]}`
	res := response2{}
	json.Unmarshal([]byte(str), &res)
	fmt.Println(res)
	fmt.Println(res.Fruits[0])

	enc := json.NewEncoder(os.Stdout)
	d := map[string]int{"apple": 5, "lettuce": 7}
	enc.Encode(d)
}

image-20230505135300690

XML

Go通过encoding.xml包提供了对XML和类XML格式的内置支持。Go语言内置的 encoding/xml 包可以用在结构体和 XML 格式之间进行编解码,其方式跟 encoding/json 包类似;然而与 JSON 相比 XML 的编码和解码在功能上更苛刻得多,这是由于 encoding/xml 包要求结构体的字段包含格式合理的标签,而 JSON 格式却不需要。

package main

import (
	"encoding/xml"
	"fmt"
)

type Plant struct {
	XMLName xml.Name `xml:"plant"`
	Id      int      `xml:"id,attr"`
	Name    string   `xml:"name"`
	Origin  []string `xml:"origin"`
}

func (p Plant) String() string {
	return fmt.Sprintf("Plant id=%v, name=%v, origin=%v",
		p.Id, p.Name, p.Origin)
}

func main() {
	coffee := &Plant{Id: 27, Name: "Coffee"}
	coffee.Origin = []string{"Ethiopia", "Brazil"}

	out, _ := xml.MarshalIndent(coffee, " ", "  ")
	fmt.Println(string(out))

	fmt.Println(xml.Header + string(out))

	var p Plant
	if err := xml.Unmarshal(out, &p); err != nil {
		panic(err)
	}
	fmt.Println(p)

	tomato := &Plant{Id: 81, Name: "Tomato"}
	tomato.Origin = []string{"Mexico", "California"}

	type Nesting struct {
		XMLName xml.Name `xml:"nesting"`
		Plants  []*Plant `xml:"parent>child>plant"`
	}

	nesting := &Nesting{}
	nesting.Plants = []*Plant{coffee, tomato}

	out, _ = xml.MarshalIndent(nesting, " ", "  ")
	fmt.Println(string(out))
}

image-20230505135543401

时间-time

在编程中经常会遭遇八小时时间差问题,这是由时区差异引起的,为了能更好地解决它们,需要先理解几个时间定义标准。

  • GMT(Greenwich Mean Time):格林威治时间;GMT 根据地球的自转和公转来计算时间,它规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点;GMT 是前世界标准时。
  • UTC(Coordinated Universal Time),协调世界时间,又称世界统一时间;UTC 比 GMT 更精准,它根据原子钟来计算时间,适应现代社会的精确计时。在不需要精确到秒的情况下,可以认为 UTC=GMT;UTC 是现世界标准时。

适应现代社会的精确计时从格林威治本初子午线起,往东为正,往西为负,全球共划分为 24 个标准时区,相邻时区相差一个小时;如何获取自Unix纪元以来的秒数、毫秒数或纳秒数和时间格式化。

package main

import (
	"fmt"
	"time"
)

func main() {
	p := fmt.Println

	now := time.Now()
	p(now)

	then := time.Date(
		2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
	p(then)

	p(then.Year())
	p(then.Month())
	p(then.Day())
	p(then.Hour())
	p(then.Minute())
	p(then.Second())
	p(then.Nanosecond())
	p(then.Location())

	p(then.Weekday())

	p(then.Before(now))
	p(then.After(now))
	p(then.Equal(now))

	diff := now.Sub(then)
	p(diff)

	p(diff.Hours())
	p(diff.Minutes())
	p(diff.Seconds())
	p(diff.Nanoseconds())

	p(then.Add(diff))
	p(then.Add(-diff))
	p("-----------------------")
	fmt.Println(now.Unix())
	fmt.Println(now.UnixMilli())
	fmt.Println(now.UnixNano())

	fmt.Println(time.Unix(now.Unix(), 0))
	fmt.Println(time.Unix(0, now.UnixNano()))
	p("-----------------------")
	t := time.Now()
	p(t.Format(time.RFC3339))

	t1, e := time.Parse(
		time.RFC3339,
		"2012-11-01T22:08:41+00:00")
	p(t1)

	p(t.Format("3:04PM"))
	p(t.Format("Mon Jan _2 15:04:05 2006"))
	p(t.Format("2006-01-02T15:04:05.999999-07:00"))
	form := "3 04 PM"
	t2, e := time.Parse(form, "8 41 PM")
	p(t2)

	fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
		t.Year(), t.Month(), t.Day(),
		t.Hour(), t.Minute(), t.Second())

	ansic := "Mon Jan _2 15:04:05 2006"
	_, e = time.Parse(ansic, "8:41PM")
	p(e)
}

image-20230505181055251

网络-net

网络编程是go语言使用的一个核心模块;golang的网络封装使用对于底层socket或者上层的http,甚至是web服务都很友好。net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket等方式的通信。其中每一种通信方式都使用 xxConn 结构体来表示,诸如IPConn、TCPConn等,这些结构体都实现了Conn接口,Conn接口实现了基本的读、写、关闭、获取远程和本地地址、设置timeout等功能。

URL

url提供了一种统一的方式来定位资源,Go中解析url需要使用到其net包。

  • URL格式:😕/:@:/ :?#
  • scheme : 方案是如何访问指定资源的主要标识符,他会告诉负责解析 URL 应用程序应该使用什么协议;
  • user :用户名;
  • password :密码;
  • host : 主机组件标识了因特网上能够访问资源的宿主机器,可以有主机名或者是 IP 地址来表示;
  • port : 端口标识了服务器正在监听的网络端口。默认端口号是 80;
  • path : URL 的路径组件说明了资源位于服务器的什么地方;
  • params : URL 中通过协议参数来访问资源,比名值对列表,分号分割来进行访问;
  • query : 字符串是通过提问问题或进行查询来缩小请求资源类的范围;
  • frag : 为了引用部分资源或资源的一个片段,比如 URL 指定 HTML 文档中一个图片或一个小节;
package main

import (
    "fmt"
    "net"
    "net/url"
)

func main() {

    s := "postgres://user:pass@host.com:5432/path?k=v#f"

    u, err := url.Parse(s)
    if err != nil {
        panic(err)
    }

    fmt.Println(u.Scheme)

    fmt.Println(u.User)
    fmt.Println(u.User.Username())
    p, _ := u.User.Password()
    fmt.Println(p)

    fmt.Println(u.Host)
    host, port, _ := net.SplitHostPort(u.Host)
    fmt.Println(host)
    fmt.Println(port)

    fmt.Println(u.Path)
    fmt.Println(u.Fragment)

    fmt.Println(u.RawQuery)
    m, _ := url.ParseQuery(u.RawQuery)
    fmt.Println(m)
    fmt.Println(m["k"][0])
}

image-20230505150102644

HTTP客户端和服务端

Go语言提供了一个功能丰富的net/http包,它提供了客户端和服务端的实现,使得我们可以比较轻易的创建http服务。

  • 作为客户端发送http请求
package main

import (
    "bufio"
    "fmt"
    "net/http"
)

func main() {

    resp, err := http.Get("https://gobyexample.com")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("Response status:", resp.Status)

    scanner := bufio.NewScanner(resp.Body)
    for i := 0; scanner.Scan() && i < 5; i++ {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        panic(err)
    }
}

image-20230505161903254

  • 作为服务端监听HTTP请求
package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {

    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func main() {

    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)

    http.ListenAndServe(":8090", nil)
}

访问测试页面:http://localhost:8090/hello和http://localhost:8090/headers

image-20230505162247985

加密

crypto是go的加密库,包含了常用的各种密码算法,AES,DES,Cipher,DSA,ecdsa,elliptic,HMAC,MD5,特别用于加密的随机数生成器rand,RC4,RSA,SHA1,SHA256,SHA384,SHA512,tls1.2,tls1.3,X.509;如SHA256哈希经常用于计算二进制或文本blob的短身份,TLS/SSL证书常使用SHA256来计算证书的签名;md5算法属于hash算法的一种。

package main

import (
	"crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
)

func main() {
	s := "sha256 this string"

	h := sha256.New()

	h.Write([]byte(s))

	bs := h.Sum(nil)

	fmt.Println(s)
	fmt.Printf("%x\n", bs)

	has := md5.New()
	has.Write([]byte("abc123"))
	b := has.Sum(nil)
	fmt.Println(b)
	fmt.Println(hex.EncodeToString(b))
	fmt.Printf("%x", b)

	c := md5.Sum([]byte("abc123"))
	fmt.Printf("%x", c)
}

image-20230505152229802

IO操作

io包提供了对I/O原语的基本接口。该包的基本任务是包装这些原语已有的实现(如os包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加了一些相关的原语的操作。

读写文件

读写文件在Go程序是常见的功能,bufio包实现了带缓冲区的读写,是对文件读写的封装.其中的读写文件在Go程序是常见的功能Go语言里使用io.Reader和io.Writer两个 interface 来抽象I/O。io.Reader 接口代表一个可以从中读取字节流的实体,而io.Writer则代表一个可以向其写入字节流的实体。io.Reader/Writer 常用的几种实现:

  • net.Conn: 表示网络连接。
  • os.Stdin, os.Stdout, os.Stderr: 标准输入、输出和错误。
  • os.File: 网络,标准输入输出,文件的流读取。
  • strings.Reader: 字符串抽象成 io.Reader 的实现。
  • bytes.Reader: []byte抽象成 io.Reader 的实现。
  • bytes.Buffer: []byte抽象成 io.Reader 和 io.Writer 的实现。
  • bufio.Reader/Writer: 带缓冲的流读取和写入(比如按行读写)。
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	writeDemo()
	readDemo()
}

func writeDemo() {

	d1 := []byte("hello\ngo\n")
	err := os.WriteFile("/tmp/dat1", d1, 0644)
	check(err)

	f, err := os.Create("/tmp/dat2")
	check(err)

	defer f.Close()

	d2 := []byte{115, 111, 109, 101, 10}
	n2, err := f.Write(d2)
	check(err)
	fmt.Printf("wrote %d bytes\n", n2)

	n3, err := f.WriteString("writes\n")
	check(err)
	fmt.Printf("wrote %d bytes\n", n3)

	f.Sync()

	w := bufio.NewWriter(f)
	n4, err := w.WriteString("buffered\n")
	check(err)
	fmt.Printf("wrote %d bytes\n", n4)

	w.Flush()

}
func readDemo() {
	dat, err := os.ReadFile("/tmp/dat1")
	check(err)
	fmt.Print(string(dat))

	f, err := os.Open("/tmp/dat1")
	check(err)

	b1 := make([]byte, 5)
	n1, err := f.Read(b1)
	check(err)
	fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))

	o2, err := f.Seek(6, 0)
	check(err)
	b2 := make([]byte, 2)
	n2, err := f.Read(b2)
	check(err)
	fmt.Printf("%d bytes @ %d: ", n2, o2)
	fmt.Printf("%v\n", string(b2[:n2]))

	o3, err := f.Seek(6, 0)
	check(err)
	b3 := make([]byte, 2)
	n3, err := io.ReadAtLeast(f, b3, 2)
	check(err)
	fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))

	_, err = f.Seek(0, 0)
	check(err)

	r4 := bufio.NewReader(f)
	b4, err := r4.Peek(5)
	check(err)
	fmt.Printf("5 bytes: %s\n", string(b4))

	f.Close()
}

image-20230505154344078

环境变量

环境变量是向Unix程序传递配置信息的通用机制。

package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {

	os.Setenv("FOO", "1")
	fmt.Println("FOO:", os.Getenv("FOO"))
	fmt.Println("BAR:", os.Getenv("BAR"))

	fmt.Println()
	for _, e := range os.Environ() {
		pair := strings.SplitN(e, "=", 2)
		fmt.Println(pair[0])
	}
}

image-20230505155532697

命令行

  • 命令行参数:程序执行参数化的常用方法。例如,运行hello。Go使用run和hello。Go程序的参数。
package main

import (
	"fmt"
	"os"
)

func main() {

	argsWithProg := os.Args
	argsWithoutProg := os.Args[1:]

	arg := os.Args[3]

	fmt.Println(argsWithProg)
	fmt.Println(argsWithoutProg)
	fmt.Println(arg)
}
go run demo.go a b c d
[./demo.exe a b c d]
[a b c d]
c
  • 命令行标记:为命令行程序指定选项的常用方法。例如,在wc -l中,-l是命令行标志。
package main

import (
	"flag"
	"fmt"
)

func main() {

	wordPtr := flag.String("word", "foo", "a string")

	numbPtr := flag.Int("numb", 42, "an int")
	forkPtr := flag.Bool("fork", false, "a bool")

	var svar string
	flag.StringVar(&svar, "svar", "bar", "a string var")

	flag.Parse()

	fmt.Println("word:", *wordPtr)
	fmt.Println("numb:", *numbPtr)
	fmt.Println("fork:", *forkPtr)
	fmt.Println("svar:", svar)
	fmt.Println("tail:", flag.Args())
}
go run demo.go -word=opt -numb=7 -fork -svar=flag
word: opt
numb: 7
fork: true
svar: flag
tail: []
  • 命令行子命令:一些命令行工具如go工具或git有许多子命令,每个子命令都有自己的一组标志;例如go build和go get是go工具的两个不同的子命令;flag包允许我们轻松地定义具有自己标志的简单子命令。
package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {

    fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
    fooEnable := fooCmd.Bool("enable", false, "enable")
    fooName := fooCmd.String("name", "", "name")

    barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
    barLevel := barCmd.Int("level", 0, "level")

    if len(os.Args) < 2 {
        fmt.Println("expected 'foo' or 'bar' subcommands")
        os.Exit(1)
    }

    switch os.Args[1] {

    case "foo":
        fooCmd.Parse(os.Args[2:])
        fmt.Println("subcommand 'foo'")
        fmt.Println("  enable:", *fooEnable)
        fmt.Println("  name:", *fooName)
        fmt.Println("  tail:", fooCmd.Args())
    case "bar":
        barCmd.Parse(os.Args[2:])
        fmt.Println("subcommand 'bar'")
        fmt.Println("  level:", *barLevel)
        fmt.Println("  tail:", barCmd.Args())
    default:
        fmt.Println("expected 'foo' or 'bar' subcommands")
        os.Exit(1)
    }
}
go run demo.go foo -enable -name=joe a1 a2
subcommand 'foo'
  enable: true
  name: joe
  tail: [a1 a2]

数据库

在Go中使用SQL或类SQL数据库通过database/sql包,它为面向行的数据库提供了一个轻量级接口;也即是在Go 的标准库中是没有数据库驱动,只提供了驱动接口,但有很多第三方实现了驱动,建议选择 go-sql-driver 这个实现是目前使用最多的,其github 地址是:https://github.com/go-sql-driver/mysql

# 命令行安装三方库 
go get github.com/go-sql-driver/mysql
package main

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

func main() {
	db, err := sql.Open("mysql", "root:123456@tcp(mysqlserver:3306)/test")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	var (
		id   int
		name string
	)
	rows, err := db.Query("select id, name from test_data where id = ?", 1)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		err := rows.Scan(&id, &name)
		if err != nil {
			log.Fatal(err)
		}
		log.Println(id, name)
	}
	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}
}

上面是匿名加载mysql驱动的,将它的包限定符别名为_,所以它导出的名称对我们的代码是不可见的;在底层,驱动程序将自己注册为database/sql包可用。

image-20230505181014653

排序-sort

  • Go的sort包实现了对内置和用户定义类型的排序,实现了包括插入排序 、 堆排序 、 快排 和 归并排序4种排序方法 ,但是并没有暴露给用户接口。sort包会根据数据选择最优的排序方法(其实只使用了3种, 归并排序 除外)。用户需要实现以下接口才能使用sort包的排序功能。对于常用的类型( 整型切片 、 float64切片 、 String切片 ),sort包提供了内置的接口实现;如经常用到的 int32、int64、float32、bool 类型并没有由 sort 包实现,使用时依然需要开发者自己编写。

  • 除了基本类型的排序,也可以对结构体进行排序。 结构体比基本类型更为复杂,排序时不能像数值和字符串一样拥有一些固定的单一原则;可以自定义排序接口,比如想要按自然顺序以外的方式对集合进行排序;例如希望按字符串的长度而不是按字母顺序排序。

  • Go 的 sort 包中所有的排序算法在最坏的情况下会做 n log n 次 比较,n 是被排序序列的长度,所以排序的时间复杂度是 O ( n log n*)。 其大多数的函数都是用改良后的快速排序算法实现的。

package main

import (
	"fmt"
	"sort"
)

type byLength []string

func (s byLength) Len() int {
	return len(s)
}
func (s byLength) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s byLength) Less(i, j int) bool {
	return len(s[i]) < len(s[j])
}

func main() {

	strs := []string{"c", "a", "b"}
	sort.Strings(strs)
	fmt.Println("Strings:", strs)

	ints := []int{7, 2, 4}
	sort.Ints(ints)
	fmt.Println("Ints:   ", ints)

	s := sort.IntsAreSorted(ints)
	fmt.Println("Sorted: ", s)

	fruits := []string{"peach", "banana", "kiwi"}
	sort.Sort(byLength(fruits))
	fmt.Println(fruits)
}

image-20230505172417244

测试和基准测试

单元测试是编写Go程序的重要组成部分,testing包提供了编写单元测试所需的工具,通过go test命令运行测试。

package main

import (
    "fmt"
    "testing"
)

func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func TestIntMinBasic(t *testing.T) {
    ans := IntMin(2, -2)
    if ans != -2 {
        t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    }
}

func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

    for _, tt := range tests {

        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}

func BenchmarkIntMin(b *testing.B) {
    for i := 0; i < b.N; i++ {
        IntMin(1, 2)
    }
}

命令行执行go test -v

image-20230505171025194

命令行执行go test -bench=.

image-20230505171125845

  • 本人博客网站IT小神 www.itxiaoshen.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/493276.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenLDAP安装报错

tip:基于centos7&#xff0c;OpenLDAP使用2.4.44版本 只要有succeeded就可以。error可以忽略 配置base.ldif文件&#xff0c;下面的空行是必须的。 dn: dcts,dccom o: ts com dc: ts objectClass: top objectClass: dcObject objectclass: organizationdn: cnManager,dcts,d…

leveldb自定义env

leveldb自定义env 未完待续。。。 由于项目需求&#xff0c;需要自定义LevelDB的env&#xff0c;也就是以块接口实现env中各个文件接口&#xff0c;在网上没找到类似的代码&#xff0c;就打算自己参照util/env_posix.cc实现一个简单的demo&#xff0c;等到功能实现差不多的时候…

ADRV9002官方例程开发过程中遇到的问题

开发环境&#xff1a;Vivado2021.2 HDL版本&#xff1a;hdl_2021_r2 GitHub - analogdevicesinc/hdl at hdl_2021_r2 no-OS版本&#xff1a;no_OS-2021_R2 GitHub - analogdevicesinc/no-OS at 2021_R2 &#xff08;PS&#xff1a;也可以用Vivado2019.1开发&#xff0c…

【漏洞复现】Joomla未授权访问漏洞(CVE-2023-23752)

文章目录 前言声明一、漏洞简介二、影响版本三、环境搭建四、漏洞分析五、漏洞复现六、修复建议前言 Joomla是一套全球知名的内容管理系统(CMS),其使用PHP语言加上MySQL数据库所开发,可以在Linux、Windows、MacOSX等各种不同的平台上运行。 声明 本篇文章仅用于漏洞复现与…

华为OD机试真题-密码强度等级【2023】【JAVA】

一、题目描述 密码按如下规则进行计分&#xff0c;并根据不同的得分为密码进行安全等级划分。 1、密码长度&#xff1a; 5 分: 小于等于4 个字符 10 分: 5 到7 字符 25 分: 大于等于8 个字符 2、字母&#xff1a; 0 分: 没有字母 10 分: 密码里的字母全都是小&#xff08;…

电脑硬盘磁盘不小心被格式化了?如何一键恢复操作教程

Easyrecovery是一款功能齐全的数据恢复软件&#xff0c;恢复内容包括&#xff1a;硬盘数据恢复、手机数据恢复、U盘数据恢复、Mac数据恢复、恢复删除文件及高级数据恢复等。当用户发生数据丢失问题时&#xff0c;第一时间想到的就是使用数据恢复软件来恢复已经丢失的文件。在众…

【机器学习】集成学习解读(ensemble learning)

【机器学习】集成学习解读&#xff08;ensemble learning&#xff09; 文章目录 【机器学习】集成学习解读&#xff08;ensemble learning&#xff09;1. 集成学习(ensemble learning)1.1 前言1.2 什么是集成学习 2. 如何得到若干个个体学习器2.1 集成学习之 Bagging2.2 集成学…

《Netty》从零开始学netty源码(五十三)之PoolThreadCache的功能

allocateNormal 在前面分析PoolArena的分配内存的方法中&#xff0c;每次分配都是先从本地线程缓存中分配&#xff0c;本地线程缓存PoolThreadCache的分配方法如下&#xff1a; 分配过程主要有两步&#xff1a; 从PoolThreadCache的缓存数组中获取相应大小的缓存cache将需要…

手残也不该敲的命令

Linux命令是一种很有趣且有用的东西&#xff0c;但在你不知道会带来什么后果的时候&#xff0c;它又会显得非常危险。所以&#xff0c;在输入某些命令前&#xff0c;请多多检查再敲回车。 rm –rf rm –rf是删除文件夹和里面附带内容的一种最快捷的方法&#xff0c;但是细微的…

mybatis拦截器统一处理createBy、createTime、updateBy等字段

一、问题描述 createBy 、createTime、updateBy等字段是我们创建表的时候经常要用到的几个字段&#xff0c;但是我们不可能每一次在增删改查的时候都手动去修改或者添加这几个字段的属性值&#xff0c;我们可以在系统层面统一处理&#xff0c;如何实现呢&#xff1f; 二、实现…

轻松掌握KubeKey单节点和集群安装k8s和kubesphere

1、KubeKey单节点AllInOne装kubesphere及k8s 如果只想安装k8s&#xff0c;注意在命令里不指定kubesphere即可。 1、设置hostname hostnamectl set-hostname k8s-node05 2、准备KubeKey export KKZONEcncurl -sfL https://get-kk.kubesphere.io | VERSIONv1.1.1 sh -chmod …

Codeforces Round 867 (Div. 3) AK题解

目录 A. TubeTube Feed&#xff08;签到&#xff09; 题意&#xff1a; 思路&#xff1a; 代码&#xff1a; B. Karina and Array&#xff08;签到&#xff09; 题意&#xff1a; 思路&#xff1a; 代码&#xff1a; C. Bun Lover&#xff08;结论&#xff09; 题意&…

C++类和对象(5)

类和对象 1.流插入和流提取的运算符重载2.const成员3.取地址及const取地址操作符重载 1.流插入和流提取的运算符重载 通过函数重载&#xff0c;可以对流插入运算符和流提取运算符进行运算符重载。<<流插入在ostream中&#xff0c;>>流提取在istream中。 ① 流提取…

SpringCloud全面学习笔记之初窥门径篇

目录 前言Docker初见小鲸鱼Docker架构Docker的安装Docker基操Dockerfile自定义镜像Docker-ComposeDocker镜像仓库 异步通信初识MQ同步通讯异步通讯MQ常见框架 RabbitMQ快速入门RabbitMQ概述和安装常见消息模型快速入门 SpringAMQPBasic Queue 简单队列模型Work Queue 工作队列模…

ChatGPT - 高效编写Prompt

文章目录 概念prompt基本结构如何编写prompt指导组合使用将指令提示、角色提示和种子词提示技术结合使用&#xff1a;将标准提示、角色提示和种子词提示技术结合使用的示例&#xff1a; 资料 概念 prompt 是给预训练语言模型 的一个线索/提示&#xff0c;更好的理解 人类的问题…

深入剖析PyTorch和TensorFlow:理解二者的区别与联系

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

java基础入门-02-【面向对象】

Java基础入门-02-【面向对象】 8、面向对象8.1. 类和对象8.1.1 类和对象的理解8.1.2 类的定义8.1.3 对象的使用8.1.4 学生对象-练习 8.2. 对象内存图8.2.1 单个对象内存图8.2.2 多个对象内存图 8.3. 成员变量和局部变量8.3.1 成员变量和局部变量的区别 8.4. 封装8.4.1 封装思想…

JavaSE基础(四)—— 数组、内存分配

目录 一、数组的定义 1. 静态初始化数组 1.1 数组的访问 1.2 数组的几个注意事项 2. 动态初始化数组 2.1 动态初始化数组的元素默认值 3. 两种初始化的的使用场景总结、注意事项说明 二、数组的遍历 三、数组的案例 1. 数组元素求和 2. 数组求最值 3. 数组…

系统辨识——最小二乘法

基本原理 数学推导 最小二乘法是通过输入数据与输出数据来拟合已知结构的函数关系&#xff0c;也就是说已知二者的函数关系&#xff0c;通过最小二乘法估计函数的相关参数。假设 x , y x,y x,y存在以下函数关系&#xff1a; 但是在实际中&#xff0c;测量数据时存在测量误差或…

JavaScript call,callee,caller,apply,bind之间的区别

&#xff08;现实是此岸&#xff0c;梦想是彼岸&#xff0c;中间隔着湍急的河流&#xff0c;行动则是架在河上的桥梁。——克雷洛夫&#xff09; call call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 MDN链接 call方法可以将一个对象属性作为…