@
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 | 格式化操作 |
go | Go 语言的词法、语法树、类型等。可通过这个包进行代码信息提取和修改 |
html | HTML 转义及模板系统 |
image | 常见图形格式的访问及生成 |
io | 实现 I/O 原始访问接口及访问封装 |
log | 用于日志记录和控制台输出 |
math | 数学库 |
net | 网络库,支持 Socket、HTTP、邮件、RPC、SMTP 等 |
os | 操作系统平台不依赖平台操作封装 |
path | 兼容各操作系统的路径操作实用函数 |
plugin | Go 1.7 加入的插件系统。支持将代码编译为插件,按需加载 |
reflect | 语言反射支持。可以动态获得代码中的类型信息,获取和修改变量的值 |
regexp | 正则表达式封装 |
runtime | 运行时接口 |
sort | 排序接口 |
strings | 字符串转换、解析及实用函数 |
sync | 提供同步原语,如互斥锁和条件变量(上篇文章有专门讲解) |
time | 时间接口 |
text | 文本模板及 Token 词法器 |
标准库的strings包提供了许多有用的与字符串相关的函数。Go字符串底层的数据结构在runtime/strings.go中定义如下:
从上面的stringStruct结构体得知其包含两个字段,一个是8个字节的万能指针,指向一个数组,数组里面存储就是实际的字符,另一个则是一个8个字节表示其长度,因此不管anyStrings多长通过unsafe.Sizeof("anyStrings")最终获取大小都是固定的16个字节。
下面是一些常见函数举例,可以到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")) }
len()
,可以用来获取切片、字符串、通道(channel)等的长度,for range
。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) } }
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") }
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) } }
使用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
Go提供了对正则表达式的内置支持,其由regexp包实现了正则表达式搜索;Go标准库使用RE2语法,RE2语法也是Python、C和Perl使用的正则表达式语法,常见函数:
regexp.MatchString()
用来匹配子字符串。下面这个例子是检查字符串是否以Golang
开头。我们使用^
来匹配字符串中以文本的开始。我们使用^Golang
作为正则表达式进行匹配。Compile()
或者 MustCompile()
创建一个编译好的正则表达式对象。假如正则表达式非法,那么Compile()
方法会返回error,而MustCompile()
编译非法正则表达式时不会返回error,而是会panic。如果你想要很好的性能,不要在使用的时候才调用Compile()
临时进行编译,而是预先调用Compile()
编译好正则表达式对象。FindString()
用来返回第一个匹配的结果。如果没有匹配的字符串,那么它会返回一个空的字符串,当然如果你的正则表达式就是要匹配空字符串的话,它也会返回空字符串。使用 FindStringIndex
或者 FindStringSubmatch
可以区分这两种情况。
FindStringIndex()
可以得到匹配的字符串在整体字符串中的索引位置。如果没有匹配的字符串,它会返回nil值。FindStringSubmatch()
除了返回匹配的字符串外,还会返回子表达式的匹配项。如果没有匹配项,则返回nil值。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)) }
encoding
包是 Go 标准库中的一个重要包,主要用于数据编码和解码。encoding
包中包含了许多常用的数据编码和解码算法,如 JSON、XML、CSV、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)) }
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) }
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)) }
在编程中经常会遭遇八小时时间差问题,这是由时区差异引起的,为了能更好地解决它们,需要先理解几个时间定义标准。
适应现代社会的精确计时从格林威治本初子午线起,往东为正,往西为负,全球共划分为 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) }
网络编程是go语言使用的一个核心模块;golang的网络封装使用对于底层socket或者上层的http,甚至是web服务都很友好。net包提供了可移植的网络I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket等方式的通信。其中每一种通信方式都使用 xxConn 结构体来表示,诸如IPConn、TCPConn等,这些结构体都实现了Conn接口,Conn接口实现了基本的读、写、关闭、获取远程和本地地址、设置timeout等功能。
url提供了一种统一的方式来定位资源,Go中解析url需要使用到其net包。
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]) }
Go语言提供了一个功能丰富的net/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) } }
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
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) }
io包提供了对I/O原语的基本接口。该包的基本任务是包装这些原语已有的实现(如os包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加了一些相关的原语的操作。
读写文件在Go程序是常见的功能,bufio包实现了带缓冲区的读写,是对文件读写的封装.其中的读写文件在Go程序是常见的功能Go语言里使用io.Reader和io.Writer两个 interface 来抽象I/O。io.Reader
接口代表一个可以从中读取字节流的实体,而io.Writer
则代表一个可以向其写入字节流的实体。io.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() }
环境变量是向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]) } }
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
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: []
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包可用。
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) }
单元测试是编写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
命令行执行go test -bench=.