zoukankan      html  css  js  c++  java
  • Go内置常用包

    strings 字符串函数

    • Contains(s, substr string) bool
      字符串s是否包含字符串substr,包含返回true
    • Split(s, sep string) []string
      将字符串s按照分隔符sep分隔为slice
    • Join(a []string, sep string) string
      字符串拼接,把slice a通过sep链接起
    • Trim(s string, cutset string) string
      在s字符串的头部和尾部去除cutset指定的字符串
    • Replace(s, old, new string, n int) string
      在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换,等于0不替换
    • Repeat(s string, count int) string
      重复s字符串count次,最后返回重复的字符串
    • Index(s, substr string) int
      在字符串s中查找sep所在的位置,返回位置值,找不到返回-1。注意返回0表示首位置

    strconv 字符串转换

    string()可以直接将字节数组([]byte)转为字符串(string)。

    Append: 其它类型追加到字符数组

    将整数等转换为字符串后,添加到现有的字节数组中。

    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    func main()  {
    	str := make([]byte, 0, 10)
    	str = strconv.AppendInt(str, -1, 10)
    	str = strconv.AppendUint(str, 1, 10) //无符号
    	fmt.Println(string(str))
    	str = strconv.AppendFloat(str, 3.14159, 'f', 2, 32)
    	fmt.Println(string(str))
    	str = strconv.AppendFloat(str, 30.14159, 'e', 3, 64)
    	fmt.Println(string(str))
    	str = strconv.AppendBool(str, true)
    	fmt.Println(string(str))
    	str = strconv.AppendQuote(str, "hello")
    	fmt.Println(string(str))
    	str = strconv.AppendQuoteRune(str, 97) //字符a对应的ascii码
    	fmt.Println(string(str))
    
    }
    

    输出:

    -11
    -113.14
    -113.143.014e+01
    -113.143.014e+01true
    -113.143.014e+01true"hello"
    -113.143.014e+01true"hello"'a'
    

    注:
    1、strconv.AppendInt(dst []byte, i int64, base int)的第三个参数是进制,这里写的 10 代表10进制。
    2、strconv.AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int)fmt是格式标记(b、e、E、f、g、G);prec代表精度(数字部分的长度,不包括指数部分);bitSize指定浮点类型(32:float32、64:float64)。
    格式标记:

    // 'b' (-ddddp±ddd,二进制指数)
    // 'e' (-d.dddde±dd,十进制指数)
    // 'E' (-d.ddddE±dd,十进制指数)
    // 'f' (-ddd.dddd,没有指数)
    // 'g' ('e':大指数,'f':其它情况)
    // 'G' ('E':大指数,'f':其它情况)
    
    // 如果格式标记为 'e','E'和'f',则 prec 表示小数点后的数字位数
    // 如果格式标记为 'g','G',则 prec 表示总的数字位数(整数部分+小数部分)
    

    3、strconv.AppendQuote(dst []byte, s string) []byte 将字符串 s 转换为"双引号"引起来的字符串,并将结果追加到 dst 的尾部,返回追加后的 []byte。其中的特殊字符将被转换为"转义字符"。

    Format: 其它类型转字符串

    把其他类型的转换为字符串。

    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    func main()  {
    	fmt.Println(strconv.FormatBool(true))
    	fmt.Println(strconv.FormatInt(1, 10))
    	fmt.Println(strconv.FormatUint(1, 10))
    	fmt.Println(strconv.FormatFloat(3.14159, 'f', 2, 32))
    }
    

    输出:

    true
    1
    1
    3.14
    

    Parse: 字符串转为其它类型

    将字符串转换为 bool float int uint类型的值,err指定是否转换成功。

    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    func main()  {
    	fmt.Println(strconv.ParseBool("true"))
    	fmt.Println(strconv.ParseInt("100", 10, 0))
    	fmt.Println(strconv.Atoi("100")) // 通常使用这个函数,而不使用 ParseInt
    	fmt.Println(strconv.ParseUint("100", 10, 0))
    	fmt.Println(strconv.ParseFloat("3.14159", 32))
    }
    

    输出:

    true <nil>
    100 <nil>
    100 <nil>
    100 <nil>
    3.141590118408203 <nil>
    

    注:
    1、strconv.ParseInt(s string, base int, bitSize int)的第三个参数bitSize是返回结果的bit大小,也就是int8 int16 int32 int64。如果给0,则使用默认值strconv.IntSize,64位机器大小是64。

    Quote 系列函数

    1、strconv.Quote(s string) string将字符串 s 转换为"双引号"引起来的字符串,其中的特殊字符将被转换为"转义字符","不可显示的字符"将被转换为"转义字符"。
    2、strconv.QuoteToASCII(s string) string 将字符串 s 转换为""引起来的 ASCII 字符串, "非 ASCII 字符"和"特殊字符"将被转换为"转义字符"。
    3、strconv.QuoteRune(r rune) string 将 Unicode 字符转换为“单引号”引起来的字符串,“特殊字符”将被转换为“转义字符”。

    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    func main()  {
    	fmt.Println(strconv.Quote(`hello go 语言
    `))
    	fmt.Println(strconv.Quote("hello go 语言
    "))
    	fmt.Println(strconv.QuoteToASCII(`hello go 语言
    `))
    	fmt.Println(strconv.QuoteRune(97))
    	fmt.Println(strconv.QuoteRuneToASCII(97))
    }
    

    输出:

    "hello go 语言\n"
    "hello go 语言
    "
    "hello go u8bedu8a00\n"
    'a'
    'a'
    

    encoding

    encoding/json

    • json.Marshal(v interface{}) ([]byte, error) 生成JSON
    • json.Unmarshal(data []byte, v interface{}) error 解析JSON到interface

    解析JSON

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type Employee struct {
    	FirstName string `json:"firstName"`
    	LastName string `json:"lastName"`
    }
    
    type EmployeeSlice struct {
    	Employees []Employee `json:"employees"`
    }
    
    func main()  {
    	str := `{"employees":[{"firstName":"Bill","lastName":"Gates"},{"firstName":"George","lastName":"Bush"}]}`
    	var res EmployeeSlice
    	json.Unmarshal([]byte(str), &res)
    	fmt.Println(res)
    	fmt.Println(res.Employees[0].FirstName)
    }
    
    

    输出:

    [{Bill Gates} {George Bush}]}
    Bill
    

    注:结构体里json:"firstName"struct tag,go的反射会读取并解析。那么具体是怎么解析JSON的呢?JSON的key是firstName,那么怎么找对应的字段呢?

    • 首先查找tag含有firstName的可导出的struct字段
    • 其次查找字段名是FirstName的导出字段
    • 最后查找类似FIrstName或者FiRstName等这样的除了首字母之外其他大小写不敏感的导出字段
    • 如果还是匹配不到,则会被忽略。

    生成JSON:

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type Employee struct {
    	FirstName string `json:"firstName"`
    	LastName string `json:"lastName"`
    }
    
    type EmployeeSlice struct {
    	Employees []Employee `json:"employees"`
    }
    
    func main()  {
    	data := EmployeeSlice{[]Employee{
    		{FirstName:"Bill", LastName:"Gates"},
    		{FirstName:"George", LastName:"Bush"},
    	}}
    	res,_ := json.Marshal(data)
    	fmt.Println(string(res))
    }
    
    

    输出:

    {"employees":[{"firstName":"Bill","lastName":"Gates"},{"firstName":"George","lastName":"Bush"}]}
    

    注意:生成JSON的时候建议配置tag,可以控制输出的是小写或者不输出。需要注意的几点是:

    • 字段的tag是"-",那么这个字段不会输出到JSON
    • tag中带有自定义名称,那么这个自定义名称会出现在JSON的字段名中,例如上面例子中firstName
    • tag中如果带有omitempty选项,那么如果该字段值为空,就不会输出到JSON串中。
    • 如果字段类型是bool, string, int, int64等,而tag中带有,string选项,那么这个字段在输出到JSON的时候会把该字段对应的值转换成JSON字符串。

    encoding/hex

    hex包实现了16进制字符表示的编解码。

    常用方法:

    • func DecodeString(s string) ([]byte, error) :将hex加密字符串解码为字节数组
    • func EncodeToString(src []byte) string :将字节数组编码为hex加密字符串

    其它:

    • func DecodedLen(x int) int :获取长度x的编码数据解码后的明文数据的长度
    • func Decode(dst, src []byte) (int, error) :将src解码为DecodedLen(len(src))字节,返回实际写入dst的字节数;如遇到非法字符,返回描述错误的error。
    • func EncodedLen(n int) int :获取长度x的明文数据编码后的编码数据的长度
    • func Encode(dst, src []byte) int :将src的数据解码为EncodedLen(len(src))字节,返回实际写入dst的字节数:EncodedLen(len(src))

    示例1:

    package main
    
    import (
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	decode_byte,_ := hex.DecodeString("3130")
    	fmt.Print(string(decode_byte))
    }
    

    输出:

    10
    

    示例2:

    package main
    
    import (
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	str := []byte("hello")
    	fmt.Print("编码前的字节数组:")
    	fmt.Println(str)
    
    	//编码为hex加密字符串
    	encode_str := hex.EncodeToString(str)
    	fmt.Println("编码后的结果:" + encode_str)
    
    	//解码
    	decode_byte,_ := hex.DecodeString(encode_str)
    	fmt.Print("解码后的字节数组:")
    	fmt.Println(decode_byte)
    }
    

    输出:

    编码前的字节数组:[104 101 108 108 111]
    编码后的结果:68656c6c6f
    解码后的字节数组:[104 101 108 108 111]
    

    示例3:

    package main
    
    import (
    	"encoding/hex"
    	"fmt"
    )
    
    func main() {
    	src := []byte("hello")
    	fmt.Print("编码前的字节数组:")
    	fmt.Println(src)
    
    	//编码为加密字符串
    	dst := make([]byte, hex.EncodedLen(len(src)))
    	hex.Encode(dst, src)
    	fmt.Println("编码后的结果:" + string(dst))
    
    	//解码
    	src = dst
    	dst2 := make([]byte, hex.DecodedLen(len(src)))
    	hex.Decode(dst2, src)
    	fmt.Print("解码后的字节数组:")
    	fmt.Println(dst2)
    }
    

    输出:

    编码前的字节数组:[104 101 108 108 111]
    编码后的结果:68656c6c6f
    解码后的字节数组:[104 101 108 108 111]
    

    crypto

    md5

    package main
    
    import (
    	"crypto/md5"
    	//"io"
    	"fmt"
    	"encoding/hex"
    )
    
    func main(){
    	h := md5.New()
    	//io.WriteString(h, "123456")
    	h.Write([]byte("123456"))
    	cipherStr := h.Sum(nil)
    	fmt.Println(cipherStr) //一个128bit的16字节byte数组
    	fmt.Println(hex.EncodeToString(cipherStr)) // 输出hex加密结果 
    }
    

    运行输出:

    [225 10 220 57 73 186 89 171 190 86 224 87 242 15 136 62]
    e10adc3949ba59abbe56e057f20f883e
    

    使用io.WriteString(h, "123456")h.Write([]byte("123456"))作用相同,可以多次Write,会把字符串追加到前一次的末尾。

    md5封装:

    func md5(str string) string {
        h := md5.New()
    	io.WriteString(h, str)
    	cipherStr := h.Sum(nil)
    	return hex.EncodeToString(cipherStr)
    }
    

    除了md5,还有sha1sha256,使用方法是类似的。

    sha1

    import "crypto/sha1"
    
    func sha1(str string) string {
        h := sha1.New()
    	io.WriteString(h, str)
    	cipherStr := h.Sum(nil)
    	return hex.EncodeToString(cipherStr)
    }
    

    sha256

    import "crypto/sha256"
    
    func sha256(str string) string {
        h := sha256.New()
    	io.WriteString(h, str)
    	cipherStr := h.Sum(nil)
    	return hex.EncodeToString(cipherStr)
    }
    

    base64

    package main
    
    import (
    	"encoding/base64"
    	"fmt"
    )
    
    // 编码
    func base64Encode(str []byte) []byte {
    	return []byte(base64.StdEncoding.EncodeToString(str))
    }
    
    // 解码
    func base64Decode(str []byte) ([]byte, error){
    	return base64.StdEncoding.DecodeString(string(str))
    }
    
    func main(){
    	str := "hello"
    	enc_str := base64Encode([]byte(str))
    	fmt.Println(enc_str)
    	fmt.Println(string(enc_str))
    
    	dec_str,err := base64Decode(enc_str)
    	if(err != nil){
    		fmt.Println(err.Error())
    	}
    
    	fmt.Println(dec_str)
    	fmt.Println(string(dec_str))
    }
    

    输出:

    [97 71 86 115 98 71 56 61]
    aGVsbG8=
    [104 101 108 108 111]
    hello
    

    AES

    time

    常见的有:

    • time.Now().Unix() int64: 返回当前本地时间戳,秒
    • time.Now().UnixNano() int64: 返回当前本地时间戳,纳秒
    • time.Second: 常量,1秒
    • time.February: 常量,二月
    • time.Sleep(time.Second):休眠1s

    Go语言里使用Time结构体保存时间信息。

    时间解析与格式化

    时间解析:

    • time.Parse(layout, value string) (Time, error): 从字符串里按模板解析成时间
    • time.layout, value string, loc *Location) (Time, error): 从字符串里按模板解析成时间

    时间格式化:

    • time.Time.Format(layout string) string: 以layout格式化输出

    说明:
    1、time.Now() 的时区是 time.Local,而 time.Parse 解析出来的时区却是 time.UTC(可以通过 Time.Location() 函数知道是哪个时区)。在中国,它们相差 8 小时。所以,一般的,我们应该总是使用 time.ParseInLocation 来解析时间,并给第三个参数传递 time.Local
    2、layout是解析的模板,类似于其他语言中 Y-m-d H:i:s 等。Go语言里是个固定值2006-01-02 15:04:05,官方说这样很好记~ 2006年1月2日3点4分5秒。

    示例:

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    
    	t,_ := time.ParseInLocation("2006-01-02 15:04:05", "2019-08-11 11:50:35", time.Local)
    
    	//输出字符串,使用默认模板:2006-01-02 15:04:05.999999999 -0700 MST
    	fmt.Println(t.String())
    
    	//输出小时
    	fmt.Println(t.Hour())
    
    	//格式化输出
    	fmt.Println(t.Format("2006-01-02"))
    }
    

    输出:

    2019-08-11 11:50:35 +0800 CST
    11
    2019-08-11
    

    注意:模板layout必须和要解析的字符串长度、分隔符一致:如果你要解析的是字符串2019/08/11,那么模板就是2006/01/02,否则解析不出来。

    时间生成

    除了使用从字符串解析的方式,还可以使用Data()方法生成:

    • time.Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time: 手动生成时间

    示例:

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    
    	t := time.Date(2019, 8, 11, 12, 11, 12, 0, time.Local)
    
    	//输出字符串,使用默认模板:2006-01-02 15:04:05.999999999 -0700 MST
    	fmt.Println(t.String())
    }
    

    输出:

    2019-08-11 12:11:12 +0800 CST
    

    Round 和 Truncate 方法

    • time.Round(d Duration) Time: 下一个(未来)接近的时间点
    • time.Truncate(d Duration) Time: 上一个(过去)接近的时间点

    Durationint64类型,直接使用time.Hour等常量即可。

    用于取整点或整分的时间。

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    
    	//t, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:00:00"), time.Local)
    	//fmt.Println(t.String()) //output: 2019-08-11 15:00:00 +0800 CST
    
    	t := time.Now()
    
    	fmt.Println(t.String()) //output: 2019-08-11 15:48:45.171627 +0800 CST m=+0.000442106
    
    	//上一个整点
    	fmt.Println(t.Truncate(time.Hour * 1)) //output: 2019-08-11 15:00:00 +0800 CST
    
    	//下一个整点
    	fmt.Println(t.Round(time.Hour * 1)) //output: 2019-08-11 16:00:00 +0800 CST
    
    	//下一个整分
    	fmt.Println(t.Round(time.Minute * 1)) //output: 2019-08-11 15:49:00 +0800 CST
    }
    

    输出:

    2019-08-11 15:53:34.019176 +0800 CST m=+0.000320602
    2019-08-11 15:00:00 +0800 CST
    2019-08-11 16:00:00 +0800 CST
    2019-08-11 15:54:00 +0800 CST
    

    简单定时器

    • time.NewTimer(d Duration) *Timer: 一次性定时器
    • time.NewTicker(d Duration) *Ticker: 周期性定时器

    一次性定时器:

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	timer := time.NewTimer(time.Second)
    
    	<- timer.C
    	fmt.Println(time.Now())
    }
    

    周期性定时器:

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    
    	ticker := time.NewTicker(time.Second * 1)
    	i := 0
    
    	for {
    		<- ticker.C //阻塞
    
    		fmt.Println(time.Now())
    
    		i++
    
    		//计数3后停止
    		if i == 3 {
    			ticker.Stop() //停止定时器
    			break //跳出循环
    		}
    	}
    
    }
    

    输出:

    2019-08-11 16:10:20.107338 +0800 CST m=+1.005101384
    2019-08-11 16:10:21.10705 +0800 CST m=+2.004785902
    2019-08-11 16:10:22.107314 +0800 CST m=+3.005023060
    

    更多:Go语言版crontab
    http://blog.studygolang.com/2014/02/go_crontab/

    net

    net/url

    URL结构体:

    type URL struct {
        Scheme   string
        Opaque   string    // 编码后的不透明数据
        User     *Userinfo // 用户名和密码信息
        Host     string    // host或host:port
        Path     string
        RawQuery string // 编码后的查询字符串,没有'?'
        Fragment string // 引用的片段(文档位置),没有'#'
    }
    

    Values表单结构体:

    type Values map[string][]string
    

    对URL进行编码和解码:

    • QueryEscape(s string) string: url编码
    • QueryUnescape(s string) (string, error): url解码

    对Path(路径)进行编码和解码:

    • PathEscape(s string) string
    • PathUnescape(s string) (string, error)

    PathEscape只对/进行编码,注意和QueryEscape的区别。

    URL操作:

    • Parse(ref string) (*URL, error): url解析

    表单Values操作:

    • Values.Get(key string) string: 获取第一个值
    • Values.Set(key, value string): 设置值
    • Values.Add(key, value string): 追加值
    • Values.Del(key string): 删除key
    • Values.Encode() string: Values表单结构体转为字符串

    详见:https://studygolang.com/pkgdoc

    net/http

    net/http已经很好的支持了常见的GET、POST请求。

    简单get请求

    package main
    
    import (
    	"net/http"
    	"fmt"
    	"io/ioutil"
    	"io"
    )
    
    func main() {
    	var url string = "http://httpbin.org/get?page=1&limit=2"
    	resp, err := http.Get(url)
    	if (err != nil) {
    		fmt.Println(err.Error())
    	}
    
    	fmt.Println(resp.Status)     //200 ok
    	fmt.Println(resp.StatusCode) //200
    
    	var bodyReader io.ReadCloser = resp.Body //返回的是io.Reader
    	body, _ := ioutil.ReadAll(bodyReader)
    	fmt.Println(string(body))
    }
    

    输出:

    200 OK
    200
    {
      "args": {
        "limit": "2", 
        "page": "1"
      }, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Connection": "close", 
        "Host": "httpbin.org", 
        "User-Agent": "Go-http-client/1.1"
      }, 
      "origin": "221.217.54.202", 
      "url": "http://httpbin.org/get?page=1&limit=2"
    }
    

    简单post表单请求

    post表单请求使用http.PostForm()。除了需要额外的参数外,其它的和get请求一样。

    package main
    
    import (
    	"net/http"
    	"fmt"
    	"io/ioutil"
    	"io"
    	"net/url"
    )
    
    func main() {
    	var apiURL string = "http://httpbin.org/post?page=1"
    	var params url.Values = url.Values{"names": []string{"yjc", "yjc1"}}
    	params.Set("age", "20")
    	resp, err := http.PostForm(apiURL, params)
    	if (err != nil) {
    		fmt.Println(err.Error())
    	}
    
    	fmt.Println(resp.Status)     //200 ok
    	fmt.Println(resp.StatusCode) //200
    
    	var bodyReader io.ReadCloser = resp.Body //返回的是io.Reader
    	body, _ := ioutil.ReadAll(bodyReader)
    	fmt.Println(string(body))
    }
    
    

    输出:

    200 OK
    200
    {
      "args": {
        "page": "1"
      }, 
      "data": "", 
      "files": {}, 
      "form": {
        "age": "20", 
        "names": [
          "yjc", 
          "yjc1"
        ]
      }, 
      "headers": {
        "Accept-Encoding": "gzip", 
        "Connection": "close", 
        "Content-Length": "27", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": "Go-http-client/1.1"
      }, 
      "json": null, 
      "origin": "221.217.54.202", 
      "url": "http://httpbin.org/post?page=1"
    }
    

    封装

    实际上,Get()Post()PostForm()Head()均是对http.Client的封装,为了快捷调用。如果需要设置Header等参数,则需要使用最原始的NewRequest了。示例:

    
    var apiURL string = "http://httpbin.org/post?page=1"
    var params url.Values = url.Values{}
    params.Set("name", "yujc")
    
    //创建客户端实例
    client := &http.Client{}
    
    //创建请求实例
    req, err := http.NewRequest("POST", apiURL, strings.NewReader(params.Encode()))
    if err != nil {
    	return nil, err
    }
    
    //增加Header
    req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Add("Content-Encoding", "gzip")
    
    //发起请求
    resp, err := client.Do(req)
    if err != nil {
    	return nil, err
    }
    
    defer resp.Body.Close()
    
    //读取响应
    fmt.Println(ioutil.ReadAll(resp.Body))
    

    方法封装:

    package util
    
    import (
    	"errors"
    	"io/ioutil"
    	"net/http"
    	"net/url"
    	"strings"
    )
    
    // params 是url.Values类型
    func Request(apiURL string, params url.Values, method string) (rs []byte, err error) {
    	client := &http.Client{}
    
    	if method == "" {
    		method = "GET"
    	}
    
    	var req *http.Request
    	if method == "GET" {
    		req, err = http.NewRequest("GET", apiURL, strings.NewReader(params.Encode()))
    		if err != nil {
    			return nil, err
    		}
    	} else if method == "POST" {
    		req, err = http.NewRequest("POST", apiURL, strings.NewReader(params.Encode()))
    		if err != nil {
    			return nil, err
    		}
    
    		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
    	} else {
    		return nil, errors.New("不支持的类型")
    	}
    
    	//发起请求
    	resp, err := client.Do(req)
    	if err != nil {
    		return nil, err
    	}
    	defer resp.Body.Close()
    
    	return ioutil.ReadAll(resp.Body)
    }
    

    测试:

    package main
    
    import (
    	"fmt"
    	"go-test/util"
    	"net/url"
    )
    
    func main() {
    	var apiURL string = "http://httpbin.org/post?page=1"
    	var params url.Values = url.Values{}
    	params.Set("name", "yujc")
    
    	body, _ := util.Request(apiURL, params, "POST");
    	fmt.Printf("%s", body)
    }
    

    net/http/pprof

    该包来做代码的性能监控。使用示例:

    package main
    
    import (
    	"net/http"
    	_ "net/http/pprof"
    )
    
    func main(){
        //提供给负载均衡探活以及pprof调试
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		w.Write([]byte("ok"))
    	})
    
    	http.ListenAndServe(":10000", nil)
    }
    

    运行之后,在浏览器打开 http://127.0.0.1:10000/debug/pprof/就能看到监控的一些信息了。生产环境一般不会按上面那么写,一般都是开个协程:

    go http.ListenAndServe(":10000", nil)
    

    如何启动 PProf 可视化界面?

    需要graphviz支持,可以到 http://www.graphviz.org/download/下载,并把bin加入到环境变量。Mac可以使用brew安装。

    下面以profile( CPU profile)为例:

    方法一:

    go tool pprof -http=:8080 http://localhost:10000/debug/pprof/profile
    

    方法二:

    go tool pprof http://localhost:10000/debug/pprof/profile
    

    然后在交互式命令行输入web即可跳转到默认浏览器。(本机测试失败,打开了Sublime text)

    参考:Golang pprof详解
    https://studygolang.com/articles/14519

    os

    os 包提供了平台无关的操作系统功能接口。

    目录操作

    • func Mkdir(name string, perm FileMode) error
      创建名称为name的目录,权限设置是perm,例如0777。
    • func MkdirAll(path string, perm FileMode) error
      根据path创建多级子目录。
    • func Remove(name string) error
      删除名称为name的目录,当目录下有文件或者其他目录时会出错。
    • func RemoveAll(path string) error
      根据path删除多级子目录,如果path是单个名称,那么该目录下的子目录全部删除。

    示例:

    package main
    
    import (
    	"os"
    	"fmt"
    )
    
    func main()  {
    	os.Mkdir("tmp", 0755)
    	os.MkdirAll("tmp/test/test2", 0755)
    	err := os.Remove("tmp")
    	if err != nil{
    		fmt.Println(err)
    	}
    	os.RemoveAll("tmp")
    }
    

    运行输出:

    remove tmp: The directory is not empty.
    

    文件操作

    • func Create(name string) (file *File, err Error)
      根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的。
    • func NewFile(fd uintptr, name string) *File
      根据文件描述符创建相应的文件,返回一个文件对象。
    • func Open(name string) (file *File, err Error)
      该方法打开一个名称为name的文件,但是是只读方式,内部实现其实调用了
      OpenFile。
    • func OpenFile(name string, flag int, perm uint32) (file *File, err Error)
      打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限
    • func (file *File) Write(b []byte) (n int, err Error)
      写入byte类型的信息到文件
    • func (file *File) WriteAt(b []byte, off int64) (n int, err Error)
      在指定位置开始写入byte类型的信息
    • func (file *File) WriteString(s string) (ret int, err Error)
      写入string信息到文件
    • func (file *File) Read(b []byte) (n int, err Error)
      读取数据到b中
    • func (file *File) ReadAt(b []byte, off int64) (n int, err Error)
      从off开始读取数据到b中
    • func Remove(name string) Error
      调用该函数就可以删除文件名为name的文件。删除文件和删除文件夹是同一个函数。

    io

    io 包为 I/O 原语提供了基本的接口。在 io 包中最重要的是两个接口:ReaderWriter 接口。只要满足这两个接口,它就可以使用 IO 包的功能。

    ReaderWriter 接口定义:

    type Reader interface {
        Read(p []byte) (n int, err error)
    }
    
    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    

    常用类型中实现了io.Readerio.Writer的接口有:os.Filestrings.Readerbufio.Reader/Writerbytes.Bufferbytes.Reader

    Closer接口定义如下:

    type Closer interface {
        Close() error
    }
    

    该接口比较简单,只有一个 Close() 方法,用于关闭数据流。

    WriteString 函数:

    func WriteString(w Writer, s string) (n int, err error)
    

    这是为了方便写入 string 类型提供的函数。WriteStrings的内容写入w中,当 w 实现了 WriteString 方法时,会直接调用该方法,否则执行 w.Write([]byte(s))

    更多查看:https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter01/01.1.html

    io/ioutil

    虽然 io 包提供了不少类型、方法和函数,但有时候使用起来不是那么方便。ioutil 包封装了一些实用的 I/O 函数,提供给外部使用的一共有1个变量,7个方法。

    • func ReadFile(filename string) ([]byte, error)
      ReadFile读取文件中的所有数据,返回读取的内容和遇到的错误。

    • func WriteFile(filename string, data []byte, perm os.FileMode) error
      WriteFile向文件写入数据,如果之前有数据则会将原来的进行清空,如果文件不存在则会以指定的权限创建该文件。

    • func ReadDir(dirname string) ([]os.FileInfo, error)
      ReadDir读取指定目录中的所有目录和文件(不包括子目录)。返回读取的文件信息列表和遇到的错误,列表是经过排序的。

    • func TempFile(dir, prefix string) (f *os.File, err error)
      TempFile在dir目录中创建一个以prefix为前缀的临时文件,并将其以读写模式打开。返回创建的文件对象和遇到的错误信息。如果dir为空,则在默认的临时目录中创建文件(参见os.TimeDir),多次调用会创建不同的临时文件,调用者可以通过f.Name()获取文件的完整路径。调用本函数所创建的临时文件,应该由调用者自己删除。

    • func TempDir(dir, prefix string) (name string, err error)
      TempDir功能是创建临时目录(其他功能和TempFile一样),返回创建的完整的目录和遇到的错误信息。

    • func ReadAll(r io.Reader) ([]byte, error)
      ReadFile读取文件中的所有数据,返回读取的数据和遇到的错误。如果读取成功,则err返回nil,而不是EOF。该方法实现了io.Reader接口的使用。

    • func NopCloser(r io.Reader) io.ReadCloser
      NopCloser将r包装为一个ReadCloser类型,但Close方法不做任何事情。

    文件复制

    我们可以使用 io.Copy()或者使用 ioutil.WriteFile()+ioutil.ReadFile()进行文件复制,但最高效的还是使用边读边写的方式:

    //打开源文件
    fileRead,err :=os.Open("/tmp/test.txt")
    if err != nil {
        fmt.Println("Open err:",err)
        return
    }
    defer fileRead.Close()
    //创建目标文件
    fileWrite,err :=os.Create("/tmp/test_copy.txt")
    if err != nil {
        fmt.Println("Create err:",err)
        return
    }
    defer fileWrite.Close()
     
    //从源文件获取数据,放到缓冲区
    buf :=make([]byte, 4096)
    //循环从源文件中获取数据,全部写到目标文件中
    for {
        n,err := fileRead.Read(buf)
        if err != nil && err == io.EOF {
             fmt.Printf("读取完毕,n = d%
    :",n)
             return
        }
        fileWrite.Write(buf[:n]) //读多少、写多少
    }
    

    sort

    常用排序

    intfloat64string 都有默认的升序排序函数:

    • sort.Ints(a []int)
    • sort.Float64s(a []float64)
    • sort.Strings(a []string)

    示例:

    package main
    
    import (
    	"fmt"
    	"sort"
    )
    
    func main() {
    	nums := []int{100, 4,99, 10}
    	sort.Ints(nums)
    
    	fmt.Println(nums)
    	fmt.Println(sort.IntsAreSorted(nums)) //是否已排序
    }
    

    输出:

    [4 10 99 100]
    true
    

    那么怎么实现这些基本类型的倒序排序呢?参考下面示例:

    package main
    
    import (
    	"fmt"
    	"sort"
    )
    
    func main() {
    	nums := []int{100, 4,99, 10}
    	//sort.IntSlice(nums) 强制转为IntSlice类型,该类型实现了sort.Interface接口
    	//sort.Reverse()将数据反转
    	//sort.Sort()参数接收一个实现了sort.Interface的类型数据
    	reverse_nums := sort.Reverse(sort.IntSlice(nums))
    	sort.Sort(reverse_nums)
    
    	fmt.Println(nums)
    	fmt.Println(sort.IntsAreSorted(nums))
    	fmt.Println(sort.IsSorted(reverse_nums))
    }
    

    输出:

    [100 99 10 4]
    false
    true
    

    floatstring倒序排序实现方式类似:

    sort.Sort(sort.Reverse(sort.Float64Slice(float8List)))
    sort.Sort(sort.Reverse(sort.StringSlice(stringList)))
    

    高级排序

    sort 包中有一个 sort.Interface 接口,该接口有三个方法 Len()Less(i,j)Swap(i,j) 。 通用排序函数 sort.Sort 可以排序任何实现了 sort.Inferface 接口的对象(变量):

    type Interface interface {
    	// Len is the number of elements in the collection.
    	Len() int
    	// Less reports whether the element with
    	// index i should sort before the element with index j.
    	Less(i, j int) bool
    	// Swap swaps the elements with indexes i and j.
    	Swap(i, j int)
    }
    

    更多查看:https://studygolang.com/articles/1598

    flag

    用于获取命令行参数。

    示例:

    package main
    
    import (
        "flag"
        "fmt"
    )
    
    func main() {
        backup_dir := flag.String("b", "", "backup path")
        debug_mode := flag.Bool("d", false, "debug mode")
    
        flag.Parse()
    
    	if *backup_dir == "" || *debug_mode == "" {
    		flag.PrintDefaults() //打印帮助
    		os.Exit(1)
    	}
    
        fmt.Println("backup_dir: ", *backup_dir)
        fmt.Println("debug_mode: ", *debug_mode)
    }
    

    详情:https://studygolang.com/pkgdoc

    sync

    sync.WaitGroup

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main(){
        for i := 0; i < 100 ; i++{
            go fmt.Println(i)
        }
        time.Sleep(time.Second)
    }
    

    上面主线程为了等待goroutine都运行完毕,不得不在程序的末尾使用time.Sleep() 来睡眠一段时间,等待其他线程充分运行。但大部分时候我们都无法预知for循环内代码运行时间的长短。这时候就不能使用time.Sleep() 来完成等待操作了。

    对于这种情况,go语言中有一个其他的工具sync.WaitGroup 能更加方便的帮助我们达到这个目的。WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait() 用来控制计数器的数量。Add(n) 把计数器设置为nDone() 每次把计数器-1wait() 会阻塞代码的运行,直到计数器地值减为0

    使用WaitGroup 将上述代码可以修改为:

    func main() {
        wg := sync.WaitGroup{}
        wg.Add(100)
        for i := 0; i < 100; i++ {
            go func(i int) {
                fmt.Println(i)
                wg.Done()
            }(i)
        }
        wg.Wait()
    }
    

    详见:Golang sync.WaitGroup的用法
    https://studygolang.com/articles/12972?fr=sidebar

    sync.Mutex

    sync.RWMutex

    sync.Once

    runtime

    runtime/debug

    用于:

    • 强制进行垃圾回收
    • 设置垃圾回收的目标百分比
    • 设置被单个go协程调用栈可使用的内存最大值
    • 设置go程序可以使用的最大操作系统线程数
    • 设置程序请求运行是只触发panic,而不崩溃
    • 垃圾收集信息的写入stats中
    • 将内存分配堆和其中对象的描述写入文件中
    • 获取go协程调用栈踪迹
    • 将堆栈踪迹打印到标准错误

    详见:go-runtime/debug
    https://www.jianshu.com/p/0b3d11f7af57

    csv

    我们可以使用 strings.Split 等方法解析CSV格式,但Go提供了更好的方法。encoding/csv 包中的 NewReader 函数返回 Reader 结构体,该结构提供了读取csv文件的API:

    package main
     
    import (
       "encoding/csv"
       "fmt"
       "io"
       "os"
    )
     
    func main() {
       file, err := os.Open("/tmp/sample.csv")
       if err != nil {
          fmt.Println("Error:", err)
          return
       }
       defer file.Close()
     
       // NewReader returns a new Reader that reads from file.
       reader := csv.NewReader(file)
     
       lineCount := 0
       for {
          // Read reads one record from the reader. The record is a slice of strings
          // with each string representing one field.
          record, err := reader.Read()
          if err == io.EOF {
             break
          } else if err != nil {
             fmt.Println("Error:", err)
             return
          }
          fmt.Println("Record", lineCount, "is", record, "and has", len(record), "fields")
          for i := 0; i < len(record); i++ {
             fmt.Println(" ", record[i])
          }
          fmt.Println()
          lineCount += 1
       }
    }
    

    ReaderFieldsPerRecord 参数是一个重要的设置。这样就可以验证每一行的单元格计数。默认情况下,当设置为0时,它被设置为第一行中的记录数。如果设置为正值,则记录的数量必须匹配。如果设置了负值,则没有单元格计数验证。

    Output:

    $ cat /tmp/sample.csv 
    aaa,bbb,ccc
    ddd,eee,fff
    
    $ go run example.go
    Record 0 is [aaa bbb ccc] and has 3 fields
      aaa
      bbb
      ccc
    
    Record 1 is [ddd eee fff] and has 3 fields
      ddd
      eee
      fff
    
  • 相关阅读:
    GIthub的小技巧
    C#中DateTime格式转换
    流程控制: if分支 while循环 for循环
    注释,输入,格式化输出,数据类型,运算符
    计算机原理简单了解
    day30
    day19
    day18 时间:time:,日历:calendar,可以运算的时间:datatime,系统:sys, 操作系统:os,系统路径操作:os.path,跨文件夹移动文件,递归删除的思路,递归遍历打印目标路径中所有的txt文件,项目开发周期
    day17跨文件夹导入模块,模块的两种被执行方式,包,直接使用包中模块,包的管理
    day16模块,导入模板完成的三件事,起别名,模块的分类,模块的加载顺序,环境变量,from...import语法导入,from...import *,链式导入,循环导入
  • 原文地址:https://www.cnblogs.com/52fhy/p/11295090.html
Copyright © 2011-2022 走看看