這壹條路 發表於 2019-8-3 15:52:00

Go内置常用包

<h2 id="strings-字符串函数">strings 字符串函数</h2>
<ul>
<li><code>Contains(s, substr string) bool</code><br>
字符串s是否包含字符串substr,包含返回true</li>
<li><code>Split(s, sep string) []string</code><br>
将字符串s按照分隔符sep分隔为slice</li>
<li><code>Join(a []string, sep        string)        string</code><br>
字符串拼接,把slice        a通过sep链接起</li>
<li><code>Trim(s string, cutset        string)        string</code><br>
在s字符串的头部和尾部去除cutset指定的字符串</li>
<li><code>Replace(s, old, new string, n int) string</code><br>
在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换,等于0不替换</li>
<li><code>Repeat(s string, count int) string</code><br>
重复s字符串count次,最后返回重复的字符串</li>
<li><code>Index(s, substr string) int</code><br>
在字符串s中查找sep所在的位置,返回位置值,找不到返回-1。注意返回0表示首位置</li>
</ul>
<h2 id="strconv-字符串转换">strconv 字符串转换</h2>
<blockquote>
<p><code>string()</code>可以直接将字节数组(<code>[]byte</code>)转为字符串(<code>string</code>)。</p>
</blockquote>
<h3 id="append-其它类型追加到字符数组">Append: 其它类型追加到字符数组</h3>
<p>将整数等转换为字符串后,添加到现有的字节数组中。</p>
<pre><code class="language-go">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))

}
</code></pre>
<p>输出:</p>
<pre><code>-11
-113.14
-113.143.014e+01
-113.143.014e+01true
-113.143.014e+01true"hello"
-113.143.014e+01true"hello"'a'
</code></pre>
<p>注:<br>
1、<code>strconv.AppendInt(dst []byte, i int64, base int)</code>的第三个参数是进制,这里写的 10 代表10进制。<br>
2、<code>strconv.AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int)</code>中<code>fmt</code>是格式标记(b、e、E、f、g、G);<code>prec</code>代表精度(数字部分的长度,不包括指数部分);<code>bitSize</code>指定浮点类型(32:float32、64:float64)。<br>
格式标记:</p>
<pre><code>// '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 表示总的数字位数(整数部分+小数部分)
</code></pre>
<p>3、<code>strconv.AppendQuote(dst []byte, s string) []byte</code> 将字符串 s 转换为"双引号"引起来的字符串,并将结果追加到 dst 的尾部,返回追加后的 <code>[]byte</code>。其中的特殊字符将被转换为"转义字符"。</p>
<h3 id="format-其它类型转字符串">Format: 其它类型转字符串</h3>
<p>把其他类型的转换为字符串。</p>
<pre><code class="language-go">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))
}
</code></pre>
<p>输出:</p>
<pre><code>true
1
1
3.14
</code></pre>
<h3 id="parse-字符串转为其它类型">Parse: 字符串转为其它类型</h3>
<p>将字符串转换为 bool float int uint类型的值,err指定是否转换成功。</p>
<pre><code class="language-go">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))
}
</code></pre>
<p>输出:</p>
<pre><code>true &lt;nil&gt;
100 &lt;nil&gt;
100 &lt;nil&gt;
100 &lt;nil&gt;
3.141590118408203 &lt;nil&gt;
</code></pre>
<p>注:<br>
1、<code>strconv.ParseInt(s string, base int, bitSize int)</code>的第三个参数<code>bitSize</code>是返回结果的bit大小,也就是int8 int16 int32 int64。如果给0,则使用默认值<code>strconv.IntSize</code>,64位机器大小是64。</p>
<h3 id="quote-系列函数">Quote 系列函数</h3>
<p>1、<code>strconv.Quote(s string) string</code>将字符串 s 转换为"双引号"引起来的字符串,其中的特殊字符将被转换为"转义字符","不可显示的字符"将被转换为"转义字符"。<br>
2、<code>strconv.QuoteToASCII(s string) string</code> 将字符串 s 转换为""引起来的 ASCII 字符串, "非 ASCII 字符"和"特殊字符"将被转换为"转义字符"。<br>
3、<code>strconv.QuoteRune(r rune) string</code> 将 Unicode 字符转换为“单引号”引起来的字符串,“特殊字符”将被转换为“转义字符”。</p>
<pre><code class="language-go">package main

import (
        "fmt"
        "strconv"
)

func main(){
        fmt.Println(strconv.Quote(`hello go 语言\n`))
        fmt.Println(strconv.Quote("hello go 语言\n"))
        fmt.Println(strconv.QuoteToASCII(`hello go 语言\n`))
        fmt.Println(strconv.QuoteRune(97))
        fmt.Println(strconv.QuoteRuneToASCII(97))
}
</code></pre>
<p>输出:</p>
<pre><code>"hello go 语言\\n"
"hello go 语言\n"
"hello go \u8bed\u8a00\\n"
'a'
'a'
</code></pre>
<h2 id="encoding">encoding</h2>
<h3 id="encodingjson">encoding/json</h3>
<ul>
<li><code>json.Marshal(v interface{}) ([]byte, error)</code> 生成JSON</li>
<li><code>json.Unmarshal(data []byte, v interface{}) error</code> 解析JSON到interface</li>
</ul>
<p>解析JSON</p>
<pre><code class="language-go">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), &amp;res)
        fmt.Println(res)
        fmt.Println(res.Employees.FirstName)
}

</code></pre>
<p>输出:</p>
<pre><code>[{Bill Gates} {George Bush}]}
Bill
</code></pre>
<blockquote>
<p>注:结构体里<code>json:"firstName"</code>是 <code>struct tag</code>,go的反射会读取并解析。那么具体是怎么解析JSON的呢?JSON的key是<code>firstName</code>,那么怎么找对应的字段呢?</p>
</blockquote>
<ul>
<li>首先查找tag含有<code>firstName</code>的可导出的struct字段</li>
<li>其次查找字段名是<code>FirstName</code>的导出字段</li>
<li>最后查找类似<code>FIrstName</code>或者<code>FiRstName</code>等这样的除了首字母之外其他大小写不敏感的导出字段</li>
<li>如果还是匹配不到,则会被忽略。</li>
</ul>
<p>生成JSON:</p>
<pre><code class="language-go">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))
}

</code></pre>
<p>输出:</p>
<pre><code class="language-json">{"employees":[{"firstName":"Bill","lastName":"Gates"},{"firstName":"George","lastName":"Bush"}]}
</code></pre>
<blockquote>
<p>注意:生成JSON的时候建议配置tag,可以控制输出的是小写或者不输出。需要注意的几点是:</p>
</blockquote>
<ul>
<li>字段的<code>tag</code>是"-",那么这个字段不会输出到<code>JSON</code>。</li>
<li><code>tag</code>中带有自定义名称,那么这个自定义名称会出现在<code>JSON</code>的字段名中,例如上面例子中<code>firstName</code>。</li>
<li><code>tag</code>中如果带有<code>omitempty</code>选项,那么如果该字段值为空,就不会输出到JSON串中。</li>
<li>如果字段类型是<code>bool</code>, <code>string</code>, <code>int</code>, <code>int64</code>等,而<code>tag</code>中带有<code>,string</code>选项,那么这个字段在输出到<code>JSON</code>的时候会把该字段对应的值转换成<code>JSON</code>字符串。</li>
</ul>
<h3 id="encodinghex">encoding/hex</h3>
<p>hex包实现了16进制字符表示的编解码。</p>
<p>常用方法:</p>
<ul>
<li><code>func DecodeString(s string) ([]byte, error)</code> :将hex加密字符串解码为字节数组</li>
<li><code>func EncodeToString(src []byte) string</code> :将字节数组编码为hex加密字符串</li>
</ul>
<p>其它:</p>
<ul>
<li><code>func DecodedLen(x int) int</code> :获取长度x的编码数据解码后的明文数据的长度</li>
<li><code>func Decode(dst, src []byte) (int, error)</code> :将src解码为<code>DecodedLen(len(src))</code>字节,返回实际写入<code>dst</code>的字节数;如遇到非法字符,返回描述错误的error。</li>
<li><code>func EncodedLen(n int) int</code> :获取长度x的明文数据编码后的编码数据的长度</li>
<li><code>func Encode(dst, src []byte) int</code> :将src的数据解码为<code>EncodedLen(len(src))</code>字节,返回实际写入<code>dst</code>的字节数:<code>EncodedLen(len(src))</code>。</li>
</ul>
<p>示例1:</p>
<pre><code class="language-go">package main

import (
        "encoding/hex"
        "fmt"
)

func main() {
        decode_byte,_ := hex.DecodeString("3130")
        fmt.Print(string(decode_byte))
}
</code></pre>
<p>输出:</p>
<pre><code>10
</code></pre>
<p>示例2:</p>
<pre><code class="language-go">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)
}
</code></pre>
<p>输出:</p>
<pre><code>编码前的字节数组:
编码后的结果:68656c6c6f
解码后的字节数组:
</code></pre>
<p>示例3:</p>
<pre><code class="language-go">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)
}
</code></pre>
<p>输出:</p>
<pre><code>编码前的字节数组:
编码后的结果:68656c6c6f
解码后的字节数组:
</code></pre>
<h2 id="crypto">crypto</h2>
<h3 id="md5">md5</h3>
<pre><code class="language-go">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加密结果
}
</code></pre>
<p>运行输出:</p>
<pre><code>
e10adc3949ba59abbe56e057f20f883e
</code></pre>
<p>使用<code>io.WriteString(h, "123456")</code>与<code>h.Write([]byte("123456"))</code>作用相同,可以多次<code>Write</code>,会把字符串追加到前一次的末尾。</p>
<p><code>md5</code>封装:</p>
<pre><code class="language-go">func md5(str string) string {
    h := md5.New()
        io.WriteString(h, str)
        cipherStr := h.Sum(nil)
        return hex.EncodeToString(cipherStr)
}
</code></pre>
<p>除了<code>md5</code>,还有<code>sha1</code>、<code>sha256</code>,使用方法是类似的。</p>
<h3 id="sha1">sha1</h3>
<pre><code class="language-go">import "crypto/sha1"

func sha1(str string) string {
    h := sha1.New()
        io.WriteString(h, str)
        cipherStr := h.Sum(nil)
        return hex.EncodeToString(cipherStr)
}
</code></pre>
<h3 id="sha256">sha256</h3>
<pre><code class="language-go">import "crypto/sha256"

func sha256(str string) string {
    h := sha256.New()
        io.WriteString(h, str)
        cipherStr := h.Sum(nil)
        return hex.EncodeToString(cipherStr)
}
</code></pre>
<h3 id="base64">base64</h3>
<pre><code class="language-go">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))
}
</code></pre>
<p>输出:</p>
<pre><code>
aGVsbG8=

hello
</code></pre>
<h3 id="aes">AES</h3>
<h2 id="time">time</h2>
<p>常见的有:</p>
<ul>
<li><code>time.Now().Unix() int64</code>: 返回当前本地时间戳,秒</li>
<li><code>time.Now().UnixNano() int64</code>: 返回当前本地时间戳,纳秒</li>
<li><code>time.Second</code>: 常量,1秒</li>
<li><code>time.February</code>: 常量,二月</li>
<li><code>time.Sleep(time.Second)</code>:休眠1s</li>
</ul>
<blockquote>
<p>Go语言里使用<code>Time</code>结构体保存时间信息。</p>
</blockquote>
<h3 id="时间解析与格式化">时间解析与格式化</h3>
<p>时间解析:</p>
<ul>
<li><code>time.Parse(layout, value string) (Time, error)</code>: 从字符串里按模板解析成时间</li>
<li><code>time.layout, value string, loc *Location) (Time, error)</code>: 从字符串里按模板解析成时间</li>
</ul>
<p>时间格式化:</p>
<ul>
<li><code>time.Time.Format(layout string) string</code>: 以layout格式化输出</li>
</ul>
<blockquote>
<p>说明:<br>
1、<code>time.Now()</code> 的时区是 <code>time.Local</code>,而 <code>time.Parse</code> 解析出来的时区却是 <code>time.UTC</code>(可以通过 <code>Time.Location()</code> 函数知道是哪个时区)。在中国,它们相差 8 小时。所以,一般的,我们应该总是使用 <code>time.ParseInLocation</code> 来解析时间,并给第三个参数传递 <code>time.Local</code>。<br>
2、<code>layout</code>是解析的模板,类似于其他语言中 <code>Y-m-d H:i:s</code> 等。Go语言里是个固定值<code>2006-01-02 15:04:05</code>,官方说这样很好记~ 2006年1月2日3点4分5秒。</p>
</blockquote>
<p>示例:</p>
<pre><code class="language-go">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"))
}
</code></pre>
<p>输出:</p>
<pre><code>2019-08-11 11:50:35 +0800 CST
11
2019-08-11
</code></pre>
<blockquote>
<p>注意:模板<code>layout</code>必须和要解析的字符串长度、分隔符一致:如果你要解析的是字符串<code>2019/08/11</code>,那么模板就是<code>2006/01/02</code>,否则解析不出来。</p>
</blockquote>
<h3 id="时间生成">时间生成</h3>
<p>除了使用从字符串解析的方式,还可以使用<code>Data()</code>方法生成:</p>
<ul>
<li><code>time.Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time</code>: 手动生成时间</li>
</ul>
<p>示例:</p>
<pre><code class="language-go">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())
}
</code></pre>
<p>输出:</p>
<pre><code>2019-08-11 12:11:12 +0800 CST
</code></pre>
<h3 id="round-和-truncate-方法">Round 和 Truncate 方法</h3>
<ul>
<li><code>time.Round(d Duration) Time</code>: 下一个(未来)接近的时间点</li>
<li><code>time.Truncate(d Duration) Time</code>: 上一个(过去)接近的时间点</li>
</ul>
<blockquote>
<p><code>Duration</code>是<code>int64</code>类型,直接使用<code>time.Hour</code>等常量即可。</p>
</blockquote>
<p>用于取整点或整分的时间。</p>
<pre><code class="language-go">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
}
</code></pre>
<p>输出:</p>
<pre><code>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
</code></pre>
<h3 id="简单定时器">简单定时器</h3>
<ul>
<li><code>time.NewTimer(d Duration) *Timer</code>: 一次性定时器</li>
<li><code>time.NewTicker(d Duration) *Ticker</code>: 周期性定时器</li>
</ul>
<p>一次性定时器:</p>
<pre><code class="language-go">package main

import (
        "fmt"
        "time"
)

func main() {
        timer := time.NewTimer(time.Second)

        &lt;- timer.C
        fmt.Println(time.Now())
}
</code></pre>
<p>周期性定时器:</p>
<pre><code class="language-go">package main

import (
        "fmt"
        "time"
)

func main() {

        ticker := time.NewTicker(time.Second * 1)
        i := 0

        for {
                &lt;- ticker.C //阻塞

                fmt.Println(time.Now())

                i++

                //计数3后停止
                if i == 3 {
                        ticker.Stop() //停止定时器
                        break //跳出循环
                }
        }

}
</code></pre>
<p>输出:</p>
<pre><code>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
</code></pre>
<blockquote>
<p>更多:Go语言版crontab<br>
http://blog.studygolang.com/2014/02/go_crontab/</p>
</blockquote>
<h2 id="net">net</h2>
<h3 id="neturl">net/url</h3>
<p><code>URL</code>结构体:</p>
<pre><code class="language-go">type URL struct {
    Scheme   string
    Opaque   string    // 编码后的不透明数据
    User   *Userinfo // 用户名和密码信息
    Host   string    // host或host:port
    Path   string
    RawQuery string // 编码后的查询字符串,没有'?'
    Fragment string // 引用的片段(文档位置),没有'#'
}
</code></pre>
<p><code>Values</code>表单结构体:</p>
<pre><code class="language-go">type Values map[]string
</code></pre>
<p>对URL进行编码和解码:</p>
<ul>
<li><code>QueryEscape(s string) string</code>: url编码</li>
<li><code>QueryUnescape(s string) (string, error)</code>: url解码</li>
</ul>
<p>对Path(路径)进行编码和解码:</p>
<ul>
<li><code>PathEscape(s string) string</code></li>
<li><code>PathUnescape(s string) (string, error)</code></li>
</ul>
<blockquote>
<p><code>PathEscape</code>只对<code>/</code>进行编码,注意和<code>QueryEscape</code>的区别。</p>
</blockquote>
<p><code>URL</code>操作:</p>
<ul>
<li><code>Parse(ref string) (*URL, error)</code>: url解析</li>
</ul>
<p>表单<code>Values</code>操作:</p>
<ul>
<li><code>Values.Get(key string) string</code>: 获取第一个值</li>
<li><code>Values.Set(key, value string)</code>: 设置值</li>
<li><code>Values.Add(key, value string)</code>: 追加值</li>
<li><code>Values.Del(key string)</code>: 删除key</li>
<li><code>Values.Encode() string</code>: Values表单结构体转为字符串</li>
</ul>
<p>详见:https://studygolang.com/pkgdoc</p>
<h3 id="nethttp">net/http</h3>
<p><code>net/http</code>已经很好的支持了常见的GET、POST请求。</p>
<h4 id="简单get请求">简单get请求</h4>
<pre><code class="language-go">package main

import (
        "net/http"
        "fmt"
        "io/ioutil"
        "io"
)

func main() {
        var url string = "http://httpbin.org/get?page=1&amp;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))
}
</code></pre>
<p>输出:</p>
<pre><code>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&amp;limit=2"
}
</code></pre>
<h4 id="简单post表单请求">简单post表单请求</h4>
<p>post表单请求使用<code>http.PostForm()</code>。除了需要额外的参数外,其它的和get请求一样。</p>
<pre><code class="language-go">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))
}

</code></pre>
<p>输出:</p>
<pre><code>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"
}
</code></pre>
<h4 id="封装">封装</h4>
<p>实际上,<code>Get()</code>、<code>Post()</code>、<code>PostForm()</code>、<code>Head()</code>均是对<code>http.Client</code>的封装,为了快捷调用。如果需要设置<code>Header</code>等参数,则需要使用最原始的<code>NewRequest</code>了。示例:</p>
<pre><code class="language-go">
var apiURL string = "http://httpbin.org/post?page=1"
var params url.Values = url.Values{}
params.Set("name", "yujc")

//创建客户端实例
client := &amp;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))
</code></pre>
<p>方法封装:</p>
<pre><code class="language-go">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 := &amp;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)
}
</code></pre>
<p>测试:</p>
<pre><code class="language-go">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)
}
</code></pre>
<h3 id="nethttppprof">net/http/pprof</h3>
<p>该包来做代码的性能监控。使用示例:</p>
<pre><code class="language-go">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)
}
</code></pre>
<p>运行之后,在浏览器打开 <code>http://127.0.0.1:10000/debug/pprof/</code>就能看到监控的一些信息了。生产环境一般不会按上面那么写,一般都是开个协程:</p>
<pre><code class="language-go">go http.ListenAndServe(":10000", nil)
</code></pre>
<p>如何启动 PProf 可视化界面?</p>
<blockquote>
<p>需要<code>graphviz</code>支持,可以到 http://www.graphviz.org/download/下载,并把bin加入到环境变量。Mac可以使用brew安装。</p>
</blockquote>
<p>下面以<code>profile</code>( CPU profile)为例:</p>
<p>方法一:</p>
<pre><code>go tool pprof -http=:8080 http://localhost:10000/debug/pprof/profile
</code></pre>
<p>方法二:</p>
<pre><code>go tool pprof http://localhost:10000/debug/pprof/profile
</code></pre>
<p>然后在交互式命令行输入<code>web</code>即可跳转到默认浏览器。(本机测试失败,打开了Sublime text)</p>
<p>参考:Golang pprof详解<br>
https://studygolang.com/articles/14519</p>
<h2 id="os">os</h2>
<p><code>os</code> 包提供了平台无关的操作系统功能接口。</p>
<h3 id="目录操作">目录操作</h3>
<ul>
<li><code>func Mkdir(name string, perm FileMode) error</code><br>
创建名称为name的目录,权限设置是perm,例如0777。</li>
<li><code>func MkdirAll(path string, perm FileMode) error</code><br>
根据path创建多级子目录。</li>
<li><code>func Remove(name string) error</code><br>
删除名称为name的目录,当目录下有文件或者其他目录时会出错。</li>
<li><code>func RemoveAll(path string) error</code><br>
根据path删除多级子目录,如果path是单个名称,那么该目录下的子目录全部删除。</li>
</ul>
<p>示例:</p>
<pre><code class="language-go">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")
}
</code></pre>
<p>运行输出:</p>
<pre><code>remove tmp: The directory is not empty.
</code></pre>
<h3 id="文件操作">文件操作</h3>
<ul>
<li><code>func Create(name string) (file *File, err Error)</code><br>
根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的。</li>
<li><code>func NewFile(fd uintptr, name string) *File</code><br>
根据文件描述符创建相应的文件,返回一个文件对象。</li>
<li><code>func Open(name string) (file *File, err Error)</code><br>
该方法打开一个名称为name的文件,但是是只读方式,内部实现其实调用了<br>
OpenFile。</li>
<li><code>func OpenFile(name string, flag int, perm uint32) (file *File, err Error)</code><br>
打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限</li>
<li><code>func (file *File) Write(b []byte) (n int, err Error)</code><br>
写入byte类型的信息到文件</li>
<li><code>func (file *File) WriteAt(b []byte, off int64) (n int, err Error)</code><br>
在指定位置开始写入byte类型的信息</li>
<li><code>func (file *File) WriteString(s string) (ret int, err Error)</code><br>
写入string信息到文件</li>
<li><code>func (file *File) Read(b []byte) (n int, err Error)</code><br>
读取数据到b中</li>
<li><code>func (file *File) ReadAt(b []byte, off int64) (n int, err Error)</code><br>
从off开始读取数据到b中</li>
<li><code>func Remove(name string) Error</code><br>
调用该函数就可以删除文件名为name的文件。删除文件和删除文件夹是同一个函数。</li>
</ul>
<h2 id="io">io</h2>
<p><code>io</code> 包为 <code>I/O</code> 原语提供了基本的接口。在 <code>io</code> 包中最重要的是两个接口:<code>Reader</code> 和 <code>Writer</code> 接口。只要满足这两个接口,它就可以使用 <code>IO</code> 包的功能。</p>
<p><code>Reader</code> 、<code>Writer</code> 接口定义:</p>
<pre><code class="language-go">type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}
</code></pre>
<p>常用类型中实现了<code>io.Reader</code> 或 <code>io.Writer</code>的接口有:<code>os.File</code>、<code>strings.Reader</code>、<code>bufio.Reader/Writer</code>、<code>bytes.Buffer</code>、<code>bytes.Reader</code> 。</p>
<p><code>Closer</code>接口定义如下:</p>
<pre><code class="language-go">type Closer interface {
    Close() error
}
</code></pre>
<p>该接口比较简单,只有一个 <code>Close()</code> 方法,用于关闭数据流。</p>
<p><code>WriteString</code> 函数:</p>
<pre><code class="language-go">func WriteString(w Writer, s string) (n int, err error)
</code></pre>
<p>这是为了方便写入 <code>string</code> 类型提供的函数。<code>WriteString</code> 将<code>s</code>的内容写入<code>w</code>中,当 <code>w</code> 实现了 <code>WriteString</code> 方法时,会直接调用该方法,否则执行 <code>w.Write([]byte(s))</code>。</p>
<p>更多查看:https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter01/01.1.html</p>
<h2 id="ioioutil">io/ioutil</h2>
<p>虽然 <code>io</code> 包提供了不少类型、方法和函数,但有时候使用起来不是那么方便。<code>ioutil</code> 包封装了一些实用的 <code>I/O</code> 函数,提供给外部使用的一共有1个变量,7个方法。</p>
<ul>
<li>
<p><code>func ReadFile(filename string) ([]byte, error)</code><br>
ReadFile读取文件中的所有数据,返回读取的内容和遇到的错误。</p>
</li>
<li>
<p><code>func WriteFile(filename string, data []byte, perm os.FileMode) error</code><br>
WriteFile向文件写入数据,如果之前有数据则会将原来的进行清空,如果文件不存在则会以指定的权限创建该文件。</p>
</li>
<li>
<p><code>func ReadDir(dirname string) ([]os.FileInfo, error)</code><br>
ReadDir读取指定目录中的所有目录和文件(不包括子目录)。返回读取的文件信息列表和遇到的错误,列表是经过排序的。</p>
</li>
<li>
<p><code>func TempFile(dir, prefix string)(f *os.File, err error)</code><br>
TempFile在dir目录中创建一个以prefix为前缀的临时文件,并将其以读写模式打开。返回创建的文件对象和遇到的错误信息。如果dir为空,则在默认的临时目录中创建文件(参见<code>os.TimeDir</code>),多次调用会创建不同的临时文件,调用者可以通过f.Name()获取文件的完整路径。调用本函数所创建的临时文件,应该由调用者自己删除。</p>
</li>
<li>
<p><code>func TempDir(dir, prefix string) (name string, err error)</code><br>
TempDir功能是创建临时目录(其他功能和TempFile一样),返回创建的完整的目录和遇到的错误信息。</p>
</li>
<li>
<p><strong><code>func ReadAll(r io.Reader) ([]byte, error)</code></strong><br>
ReadFile读取文件中的所有数据,返回读取的数据和遇到的错误。如果读取成功,则err返回nil,而不是EOF。该方法实现了<code>io.Reader</code>接口的使用。</p>
</li>
<li>
<p><code>func NopCloser(r io.Reader) io.ReadCloser</code><br>
NopCloser将r包装为一个ReadCloser类型,但Close方法不做任何事情。</p>
</li>
</ul>
<h2 id="文件复制">文件复制</h2>
<p>我们可以使用 <code>io.Copy()</code>或者使用 <code>ioutil.WriteFile()</code>+<code>ioutil.ReadFile()</code>进行文件复制,但最高效的还是使用边读边写的方式:</p>
<pre><code class="language-go">//打开源文件
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 &amp;&amp; err == io.EOF {
         fmt.Printf("读取完毕,n = d%\n:",n)
         return
    }
    fileWrite.Write(buf[:n]) //读多少、写多少
}
</code></pre>
<h2 id="sort">sort</h2>
<h3 id="常用排序">常用排序</h3>
<p><code>int</code> 、 <code>float64</code> 和 <code>string</code> 都有默认的升序排序函数:</p>
<ul>
<li><code>sort.Ints(a []int)</code></li>
<li><code>sort.Float64s(a []float64)</code></li>
<li><code>sort.Strings(a []string)</code></li>
</ul>
<p>示例:</p>
<pre><code class="language-go">package main

import (
        "fmt"
        "sort"
)

func main() {
        nums := []int{100, 4,99, 10}
        sort.Ints(nums)

        fmt.Println(nums)
        fmt.Println(sort.IntsAreSorted(nums)) //是否已排序
}
</code></pre>
<p>输出:</p>
<pre><code>
true
</code></pre>
<p>那么怎么实现这些基本类型的倒序排序呢?参考下面示例:</p>
<pre><code class="language-go">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))
}
</code></pre>
<p>输出:</p>
<pre><code>
false
true
</code></pre>
<p><code>float</code>、<code>string</code>倒序排序实现方式类似:</p>
<pre><code class="language-go">sort.Sort(sort.Reverse(sort.Float64Slice(float8List)))
sort.Sort(sort.Reverse(sort.StringSlice(stringList)))
</code></pre>
<h3 id="高级排序">高级排序</h3>
<p><code>sort</code> 包中有一个 <code>sort.Interface</code> 接口,该接口有三个方法 <code>Len()</code> 、 <code>Less(i,j)</code> 和 <code>Swap(i,j)</code> 。 通用排序函数 <code>sort.Sort</code> 可以排序任何实现了 <code>sort.Inferface</code> 接口的对象(变量):</p>
<pre><code class="language-go">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)
}
</code></pre>
<p>更多查看:https://studygolang.com/articles/1598</p>
<h2 id="flag">flag</h2>
<p>用于获取命令行参数。</p>
<p>示例:</p>
<pre><code class="language-go">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)
}
</code></pre>
<p>详情:https://studygolang.com/pkgdoc</p>
<h2 id="sync">sync</h2>
<h3 id="syncwaitgroup">sync.WaitGroup</h3>
<pre><code class="language-go">package main

import (
    "fmt"
    "time"
)

func main(){
    for i := 0; i &lt; 100 ; i++{
      go fmt.Println(i)
    }
    time.Sleep(time.Second)
}
</code></pre>
<p>上面主线程为了等待<code>goroutine</code>都运行完毕,不得不在程序的末尾使用<code>time.Sleep()</code> 来睡眠一段时间,等待其他线程充分运行。但大部分时候我们都无法预知<code>for</code>循环内代码运行时间的长短。这时候就不能使用<code>time.Sleep()</code> 来完成等待操作了。</p>
<p>对于这种情况,go语言中有一个其他的工具<code>sync.WaitGroup</code> 能更加方便的帮助我们达到这个目的。<code>WaitGroup</code> 对象内部有一个计数器,最初从0开始,它有三个方法:<code>Add()</code>, <code>Done()</code>, <code>Wait()</code> 用来控制计数器的数量。<code>Add(n)</code> 把计数器设置为<code>n</code> ,<code>Done()</code> 每次把计数器<code>-1</code> ,<code>wait()</code> 会阻塞代码的运行,直到计数器地值减为<code>0</code>。</p>
<p>使用WaitGroup 将上述代码可以修改为:</p>
<pre><code class="language-go">func main() {
    wg := sync.WaitGroup{}
    wg.Add(100)
    for i := 0; i &lt; 100; i++ {
      go func(i int) {
            fmt.Println(i)
            wg.Done()
      }(i)
    }
    wg.Wait()
}
</code></pre>
<p>详见:Golang sync.WaitGroup的用法<br>
https://studygolang.com/articles/12972?fr=sidebar</p>
<h3 id="syncmutex">sync.Mutex</h3>
<h3 id="syncrwmutex">sync.RWMutex</h3>
<h3 id="synconce">sync.Once</h3>
<h2 id="runtime">runtime</h2>
<h3 id="runtimedebug">runtime/debug</h3>
<p>用于:</p>
<ul>
<li>强制进行垃圾回收</li>
<li>设置垃圾回收的目标百分比</li>
<li>设置被单个go协程调用栈可使用的内存最大值</li>
<li>设置go程序可以使用的最大操作系统线程数</li>
<li>设置程序请求运行是只触发panic,而不崩溃</li>
<li>垃圾收集信息的写入stats中</li>
<li>将内存分配堆和其中对象的描述写入文件中</li>
<li>获取go协程调用栈踪迹</li>
<li>将堆栈踪迹打印到标准错误</li>
</ul>
<p>详见:go-runtime/debug<br>
https://www.jianshu.com/p/0b3d11f7af57</p>
<h2 id="csv">csv</h2>
<p>我们可以使用 <code>strings.Split</code> 等方法解析CSV格式,但Go提供了更好的方法。<code>encoding/csv</code> 包中的 <code>NewReader</code> 函数返回 <code>Reader</code> 结构体,该结构提供了读取csv文件的API:</p>
<pre><code>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 &lt; len(record); i++ {
         fmt.Println(" ", record)
      }
      fmt.Println()
      lineCount += 1
   }
}
</code></pre>
<p><code>Reader</code> 的 <code>FieldsPerRecord</code> 参数是一个重要的设置。这样就可以验证每一行的单元格计数。默认情况下,当设置为0时,它被设置为第一行中的记录数。如果设置为正值,则记录的数量必须匹配。如果设置了负值,则没有单元格计数验证。</p>
<p>Output:</p>
<pre><code class="language-bash">$ cat /tmp/sample.csv
aaa,bbb,ccc
ddd,eee,fff

$ go run example.go
Record 0 is and has 3 fields
aaa
bbb
ccc

Record 1 is and has 3 fields
ddd
eee
fff
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    (本文完)
<br><br>


欢迎关注公众号"飞鸿影记(fhyblog)",探寻物件背后的逻辑,记录生活真实的影子。
<div style="text-align: center"><span style="color: #808000"><img src="https://images2015.cnblogs.com/blog/663847/201703/663847-20170331215930477-1406562582.jpg" alt="" width="250" height="250"></span></div>


<div>
<p><strong>作者:飞鸿影</strong></p>
<p><strong>出处:http://52fhy.cnblogs.com/</strong></p>
</div>

<hr>

<p style="border: silver 1px dashed; padding: 8px 5px; font-weight: bold">版权申明:没有标明转载或特殊申明均为作者原创。本文采用以下协议进行授权,自由转载 - 非商用 - 非衍生 - 保持署名 | Creative Commons BY-NC-ND 3.0,转载请注明作者及出处。</p>

<hr>


<div style="text-align: center;display:none"><span style="color: #808000"><img src="https://images2018.cnblogs.com/blog/663847/201805/663847-20180515232241445-706573186.png" alt="" width="250" height="250"></span></div><br><br>
来源:https://www.cnblogs.com/52fhy/p/11295090.html
頁: [1]
查看完整版本: Go内置常用包