张子阳的博客

首页 读书 技术 店铺 关于
张子阳的博客 首页 读书 技术 关于

Go语言Tips

2017-12-13 张子阳 分类: Go 语言

转型到Go以后,因为语言的不熟悉,以往很常见的一些操作有时候也需要去Google一下。这里将一些结果记录下来,方便日后查阅。

这篇文章没有什么讲解,都是一些小例子,有点类似于:go by example

如何获取Go routine的ID

不像其他语言,go本身不提供类似Thread.CurrentTread.ThreadId这样的函数,因此无法直接获得go routine的ID。但在调试时,我们有时候需要输出Go routine的ID,可以使用这个方法:

func GoID() int {
    var buf [64]byte
    n := runtime.Stack(buf[:], false)
    idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
    id, err := strconv.Atoi(idField)
    if err != nil {
        panic(fmt.Sprintf("无法获得: %v", err))
    }
    return id
}

func runTest() {
    for i := 0; i < 10; i++ {
        go func() {
            fmt.Println("GoId:", GoID())
        }()
    }
}

其实在调试时,我们主要关心的是能够区分出各个Go routine,而不关心Id的具体值。此时,可以用下面的方法,自己给每个routine提供一个id,这个方法更简单,也就不需要GoID方法了。

func runTest() {
    for i := 0; i < 10; i++ {
        go func(id index) {
            fmt.Println("GoId:", id)
        }(i)
    }
}

如何判断interface{}的类型

有时候,我们需要一个通用的方法,接收interface{}类型,然后再进行类型转换。在go中,可以通过称为type switch的语法来完成:

func typeSwitch(x interface{}) {
    switch a := x.(type) {
    case int:
        fmt.Printf("%v is int\n", a)
    case string:
        fmt.Printf("%v is string\n", a)
    default:
        fmt.Printf("%v is interface{}\n", a)
    }
}

上面这段代码的神奇之处在于:你不需要再进行类型转换,变量a就已经转换为了相应的类型。即:在case int:代码段中a是int(可以调用接受int类型的函数);在 case string: 代码段中,a又变成了string。

如何解析XML?

我没有找到类似C#中的xml API可以动态地解析xml结构, 或者通过XPath来对xml结构进行搜索。只能一次性映射成一个struct,然后通过struct的属性去访问,这样每次解析都要构建一个对应的struct,稍显麻烦,可能有更好的解决方案,只是我不知道吧。

好处是,即使xml和struct并不完全对应,也可以正确解析,否则就会变得无比麻烦。

// Root 根节点 type Root struct { XMLName xml.Name `xml:"xml"` ReturnCode string `xml:"return_code"` ReturnMsg string `xml:"return_msg"` ResultCode string `xml:"result_code"` ErrCode string `xml:"err_code"` ErrCodeDes string `xml:"err_code_des"` CodeURL string `xml:"code_url"` } func testXML() { xmlStr := `<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx2421b1c4370ec43b]]></appid> <mch_id><![CDATA[10000100]]></mch_id> <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str> <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid> <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id> <trade_type><![CDATA[Native]]></trade_type> <code_url><![CDATA[weixin://wxpay/s/An4baqw]]></code_url> </xml>` xmlByte := []byte(xmlStr) var root Root xml.Unmarshal(xmlByte, &root) fmt.Println(root.ReturnCode) fmt.Println(root.CodeURL) fmt.Println("root.ErrCode", root.ErrCode) // 这个是没有的 }

如何将各种类型转换成字符串?

Go的字符串类型转换有时候真的是让萌新有点犯晕。

func convertToString() { var text string // int 转换成字符串 text = strconv.Itoa(10000) fmt.Println("text:", text) // interface{} 转换成字符串 var i interface{} i = "hello" text = i.(string) fmt.Println("text:", text) // byte[] 转换成字符串 data := []byte("world") text = string(data) fmt.Println("text:", text) }

如何生成随机数?如何用某一字符补齐特定长度字符串?

这个小小的以日期+随机数作为唯一编号的函数包含了这两个功能。

func GetDateNo() string { // 生成随机数:0~9999 src := rand.NewSource(time.Now().UnixNano()) r := rand.New(src) suffix := strconv.Itoa(r.Intn(10000)) // 最大长度为4,并且用0来补齐 if len(suffix) < 4 { suffix = fmt.Sprintf("%04s", suffix) } str := time.Now().Format("060102150405") + suffix return str }

如何对map按照key进行排序?

无法对map进行排序,如果需要按顺序处理map,可以用下面的方法间接进行。

// 对map进行排序 func sortMap(dic map[string]string) { var keys []string for k := range dic { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { // 在这里进行处理 fmt.Println(k, ":", dic[k]) } }

如何生成Md5加密字符串?

// GetMd5 获得md5字符串 func GetMd5(text string) string { data := []byte(text) cryptoData := md5.Sum(data) cryptoText := hex.EncodeToString(cryptoData[:]) return cryptoText }

如何获取当前程序的运行目录?

func main() { dir, err := filepath.Abs(filepath.Dir(os.Args[0])) if err != nil { log.Fatal(err) } fmt.Println("Path:", dir) }

如何获取当前的GOPATH路径?

func main(){ fmt.Println(build.Default.GOPATH) }

如何发起HttpGet请求,并以字符串返回结果?

// HTTPGet 发起HttpGet请求,并返回string格式的结果 func HTTPGet(url string) (string, error) { res, err := http.Get(url) if err != nil { log.Error(err.Error(), "HTTPGet") return "", err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { log.Error(err.Error(), "HTTPGet") return "", err } return string(body), nil }

如何发起HttpPost请求,并以字符串返回结果?

// HTTPPost 发起http post请求 func HTTPPost(url string, body string) (string, error) { data := []byte(body) reader := bytes.NewReader(data) res, err := http.Post(url, "application/x-www-form-urlencoded", reader) if err != nil { log.Error(err.Error(), "HTTPPost-1") return "", err } resBody, err := ioutil.ReadAll(res.Body) if err != nil { log.Error(err.Error(), "HTTPPost-2") return "", err } return string(resBody), nil }

如何生成QR二维码

首先 go get github.com/boombuler/barcode,然后看下面范例。

package main import ( "bytes" "fmt" "image/png" "os" "github.com/boombuler/barcode" "github.com/boombuler/barcode/qr" )

生成QR图片

func createQRImage() { qrCode, _ := qr.Encode("http://www.baidu.com", qr.M, qr.Auto) qrCode, _ = barcode.Scale(qrCode, 200, 200) file, _ := os.Create("qrcode.png") defer file.Close() png.Encode(file, qrCode) }

生成QR byte数组(应该更常用一些,可以直接http返回)

func createQRByte() []byte { qrCode, _ := qr.Encode("http://www.baidu.com", qr.M, qr.Auto) qrCode, _ = barcode.Scale(qrCode, 200, 200) buf := new(bytes.Buffer) png.Encode(buf, qrCode) bytes := buf.Bytes() return bytes }

如何正确转换日期?

因为我们的时区是UTC+8,所以关于时间,如果不小心处理就会出现相差8小时的错误。看下面的代码:

func main() { const LayOut = "2006-01-02 15:04:05" // 获取当前日期并转换为字符串打印 now := time.Now() nowStr := now.Format(LayOut) fmt.Println(nowStr) // 将字符串转换为日期,再转换回去然后打印 now2, _ := time.Parse(LayOut, nowStr) now2Str := now2.Format(LayOut) fmt.Println(now2Str) diff := now.Sub(now2) fmt.Println("total :", diff.Hours()) }

按正常理解,这样转换后的结果应该是完全一致的,结果实际上now比now2早了8小时。

2017-12-14 14:53:00 2017-12-14 14:53:00 total : -7.999736999166666

字符串的值完全相等,但是diff一下发现实际不一样。其实将上面的例子稍微修改一下,就知道差别了:

// 相同代码省略 fmt.Println(now.String()) fmt.Println(now2.String())

输出为:

2017-12-14 15:05:12.3921453 +0800 CST 2017-12-14 15:05:12 +0000 UTC total : -7.99989107075

可以看到两个时间是不一样的,所以在从字符串转换为日期的时候,需要将当前时区传进去,这里是正确版本:

Beijing, _ := time.LoadLocation("Asia/Shanghai") now2, _ := time.ParseInLocation(LayOut, nowStr, Beijing) fmt.Println(now2.String())

如何获取当前URL?

本想通过url.URL获取当前URL全路径,结果本地(localhost)测试时,scheme为空,host为空,所以通过http.Request来获得,结果scheme还是空。没有啥其他办法,暂时写死了。

func getFullURL(r *http.Request) string { return "http://" + r.Host + r.RequestURI // return r.URL.Scheme + "://" + r.Host + r.RequestURI }

感谢阅读,希望这篇文章能给你带来帮助!