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 語言\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))
}

輸出:

"hello go 語言\\n"
"hello go 語言\n"
"hello go \u8bed\u8a00\\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:",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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM