Go 处理时间


package main

import (
    "fmt"
"strconv"
    "time"
)

// TimeUnix is time for unix
func TimeUnix() string {
naiveTime := time.Now().Unix()
naiveTimeString := strconv.FormatInt(naiveTime, 10)
return naiveTimeString
}

func main() {
    timeNow := time.Now()
    fmt.Println("tNow(time format): ", timeNow)
    
    // 时间转化为 string layout 必须为 "2006-01-02 15:04:05"
    timeNowStr := timeNow.Format("2006-01-02 15:04:05")
    fmt.Println("tNow(string format): ", timeNowStr)

    timeNowUnixInt := timeNow.Unix()
    fmt.Println(timeNowUnixInt)

    // string 转化为时间 layout 必须为 "2006-01-02 15:04:05"
    t1, _ := time.Parse("2006-01-02 15:04:05", "2014-06-15 08:37:18")
    fmt.Println("t(time format): ", t1)
    t2 := time.Unix(1389058332, 0).Format("2006-01-02 15:04:05")
    fmt.Println("t(time format): ", t2)
}
阅读全文

Go Error 处理


Go 中关于错误的哲学,应该尽可能优雅地处理错误。

先说结论:

  • 使用"隐藏内部细节的错误处理"
  • 使用 errors.Wrap 封装原始 error
  • 使用 errors.Cause 找出原始 error
  • 为了行为而断言,而不是类型
  • 尽量减少错误值的使用

error 的类型是 interface

type error interface {
    Error() string
}

创建error:

// example 1
func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // implementation
}

// example 2
if f < 0 {
    return 0, fmt.Errorf("math: square root of negative number %g", f)
}

阅读全文

Go 操作文件


判断文件是否存在 存在返回 true 不存在返回 false

func checkFileIsExist(filename string) bool {
    var exist = true
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        exist = false
    }
    return exist
}

打开文件 返回文件指针

file, error := os.Open("/tmp/1.txt")
if error != nil {
    fmt.Println(error)
}
fmt.Println(file)
file.Close()

以读写方式打开文件 返回文件指针 如果不存在则创建

file2, error := os.OpenFile("/tmp/2.txt", os.O_RDWR|os.O_CREATE, 0644)
if error != nil {
    fmt.Println(error)
}
fmt.Println(file2)
file2.Close()

创建文件 Create 函数也是调用的 OpenFile

file3, error := os.Create("/tmp/3.txt")
if error != nil {
    fmt.Println(error)
}
fmt.Println(file3)
file3.Close()

删除文件

del := os.Remove("/tmp/1.txt")
if del != nil {
    fmt.Println(del)
}

删除指定 path 下的所有文件

delDir := os.RemoveAll("/tmp/testdir")
if delDir != nil {
        fmt.Println(delDir)
}

阅读全文

Go 使用 base64


package main

import (
    "encoding/base64"
    "fmt"
    "log"
)

func main() {
    input := []byte("hello golang base64")

    // base64 encode
    encodeString := base64.StdEncoding.EncodeToString(input)
    fmt.Println(encodeString)

    // base decode
    decodeBytes, err := base64.StdEncoding.DecodeString(encodeString)
    if err != nil {
        log.Fatalln(err)
    }
    fmt.Println(string(decodeBytes))
}
阅读全文

Go 使用 clientv3 连接 etcd


etcd3 和 etcd2 的 api 不太一样。
etcd3 移除了目录概念,只有单纯的 kv。
api 也只有 rpc 模式,http 需要 proxy。

package etcd

import (
    "context"
    "log"
    "time"

    "../conf"

    "go.etcd.io/etcd/clientv3"
)

var (
    endpoints   = make([]string, 1)
    dialTimeout = 3 * time.Second
    username    string
    password    string
)

// Init is init etcd
func Init() {
    endpoints = append(endpoints, conf.RunTimeInfo.EtcdHost)
    username = conf.RunTimeInfo.EtcdUser
    password = conf.RunTimeInfo.EtcdPasswd
}

// connetEtcd is connect etcd
func connetEtcd() *clientv3.Client {
    cli, cliErr := clientv3.New(clientv3.Config{
        Endpoints:   endpoints,
        DialTimeout: dialTimeout,
        Username:    username,
        Password:    password,
    })
    if cliErr != nil {
        log.Println(cliErr)
    }
    return cli
}

// KeepAlive is make etcd keepalive with ttl
func KeepAlive(keyID clientv3.LeaseID) {
    cli := connetEtcd()
    if cli != nil {
        ka, kaErr := cli.KeepAliveOnce(context.TODO(), keyID)
        if kaErr != nil {
            log.Println(kaErr)
        }
        log.Println("ttl:", ka)
        cli.Close()
    }
}

// GetKV is get kv from etcd
func GetKV(dir string) []string {
    cli := connetEtcd()
    var kvList = make([]string, 0)
    if cli != nil {
        resp, respErr := cli.Get(context.TODO(), dir, clientv3.WithPrefix())
        if respErr != nil {
            log.Println(respErr)
        }
        for _, ev := range resp.Kvs {
            kvTemp := string(ev.Key) + ":" + string(ev.Value)
            kvList = append(kvList, kvTemp)
        }
        cli.Close()
    }
    return kvList
}

// PutKV is set kv to etcd
func PutKV(writePath string, writeValue string) {
    cli := connetEtcd()
    if cli != nil {
        resp, respErr := cli.Put(context.TODO(), writePath, writeValue)
        if respErr != nil {
            log.Println(respErr)
        }
        if resp != nil {
            log.Println(resp.Header)
            cli.Close()
        }
    }
}

// PutKVTTL is set kv to etcd with ttl
func PutKVTTL(registKey, registValue string) {
    cli := connetEtcd()
    if cli != nil {
        resp, respErr := cli.Grant(context.TODO(), 10)
        if respErr != nil {
            log.Println(respErr)
        }
        conf.ServID = resp.ID
        putResp, putErr := cli.Put(context.TODO(), registKey, registValue, clientv3.WithLease(resp.ID))
        if putErr != nil {
            log.Println(putErr)
        }
        log.Println(putResp.Header)
        cli.Close()
    }
}

// DelKV is del kv to etcd
func DelKV(writePath string) {
    cli := connetEtcd()
    if cli != nil {
        resp, respErr := cli.Delete(context.TODO(), writePath)
        if respErr != nil {
            log.Println(respErr)
        }
        log.Println(resp.Header)
        cli.Close()
    }
}
阅读全文

Go json 库 json-iterator


高效json库.png

直接替换 json.Marshal 替为 jsoniter.Marshal

type ColorGroup struct {
    ID     int
    Name   string
    Colors []string
}
group := ColorGroup{
    ID:     1,
    Name:   "Reds",
    Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
}
b, err := jsoniter.Marshal(group)

阅读全文

Go json 反序列化成 interface{} 对 Number 的处理


json 的规范中,对于数字类型,并不区分是整型还是浮点型。

go json number.gif

对于如下 json 文本:

{
    "name": "ethancai",
    "fansCount": 9223372036854775807
}

如果反序列化的时候指定明确的结构体和变量类型

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name      string
    FansCount int64
}

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    // 类型为User
    var user User
    err := json.Unmarshal([]byte(jsonStream), &user)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Printf("%+v \n", user)
}

Output:

{Name:ethancai FansCount:9223372036854775807}

如果反序列化不指定结构体类型或者变量类型,则 json 中的数字类型,默认被反序列化成 float64 类型:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    // 不指定反序列化的类型
    var user interface{}
    err := json.Unmarshal([]byte(jsonStream), &user)
    if err != nil {
        fmt.Println("error:", err)
    }
    m := user.(map[string]interface{})
    fansCount := m["fansCount"]
    fmt.Printf("%+v \n", reflect.TypeOf(fansCount).Name())
    fmt.Printf("%+v \n", fansCount.(float64))
}

Output:

float64
9.223372036854776e+18

另一个程序

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name      string
    // 不指定 FansCount 变量的类型
    FansCount interface{}
}

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    var user User
    err := json.Unmarshal([]byte(jsonStream), &user)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Printf("%+v \n", user)
}

Output:

{Name:ethancai FansCount:9.223372036854776e+18}

从上面的程序可以发现,如果 fansCount 精度比较高,反序列化成 float64 类型的数值时存在丢失精度的问题。

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
    "strings"
)

func main() {
    const jsonStream = `{"name":"ethancai", "fansCount": 9223372036854775807}`
    decoder := json.NewDecoder(strings.NewReader(jsonStream))
    // UseNumber causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
    decoder.UseNumber()
    var user interface{}
    if err := decoder.Decode(&user); err != nil {
        fmt.Println("error:", err)
        return
    }
    m := user.(map[string]interface{})
    fansCount := m["fansCount"]
    fmt.Printf("%+v \n", reflect.TypeOf(fansCount).PkgPath() + "." + reflect.TypeOf(fansCount).Name())
    v, err := fansCount.(json.Number).Int64()
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Printf("%+v \n", v)
}

Output:

encoding/json.Number
9223372036854775807

上面的程序,使用了 func (*Decoder) UseNumber 方法告诉反序列化 json 的数字类型的时候,不要直接转换成 float64,而是转换成 json.Number 类型。
json.Number 内部实现机制:

// A Number represents a JSON number literal.
type Number string

// String returns the literal text of the number.
func (n Number) String() string { return string(n) }

// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
    return strconv.ParseFloat(string(n), 64)
}

// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
    return strconv.ParseInt(string(n), 10, 64)
}

json.Number 本质是字符串,反序列化的时候将 json 的数值先转成 json.Number,其实是一种延迟处理的手段,待后续逻辑需要时候,再把 json.Number 转成 float64 或者 int64。

阅读全文

Go 处理 json


json to struct

import (
    "testing"
    "encoding/json"
)

// 这里对应的 N 和 A 不能为小写,首字母必须为大写,这样才可对外提供访问,具体 json 匹配是通过后面的 tag 标签进行匹配的,与 N 和 A 没有关系
// tag 标签中 json 后面跟着的是字段名称,都是字符串类型,要求必须加上双引号
type Person struct {
    N string     `json:"name"`
    A int        `json:"age"`
}

func TestStruct2Json(t *testing.T) {
    jsonStr := `{
        "name":"liangyongxing",
        "age":12
    }`
    var person Person
    json.Unmarshal([]byte(jsonStr), &person)
    t.Log(person)
}

阅读全文

Go convert


[]string to string

package main

import (
    "fmt"
    "strings"
)

var naiveList string

func main() {
    naiveArray := []string{"a", "b", "c", "d", "e"}
    for _, v := range naiveArray {
        if naiveList == "" {
        naiveList = v
        } else {
        naiveList = strings.Join([]string{naiveList, v}, ",")
        }
    }
    fmt.Println(naiveList)
}

map to struct

import (
    "testing"
    "github.com/goinggo/mapstructure"
)

func TestMap2Struct(t *testing.T) {
    mapInstance := make(map[string]interface{})
    mapInstance["Name"] = "liang637210"
    mapInstance["Age"] = 28
    var person Person
    // 将 map 转换为指定的结构体
    if err := mapstructure.Decode(mapInstance, &person); err != nil {
        t.Fatal(err)
    }
    t.Logf("map2struct 后得到的 struct 内容为:%v", person)
}

struct to map

import (
    "testing"
    "reflect"
)

type User struct {
    Id        int    `json:"id"`
    Username    string    `json:"username"`
    Password    string    `json:"password"`
}

func Struct2Map(obj interface{}) map[string]interface{} {
    t := reflect.TypeOf(obj)
    v := reflect.ValueOf(obj)
    var data = make(map[string]interface{})
    for i := 0; i < t.NumField(); i++ {
        data[t.Field(i).Name] = v.Field(i).Interface()
    }
    return data
}

func TestStruct2Map(t *testing.T) {
    user := User{5, "zhangsan", "password"}
    data := Struct2Map(user)
    t.Logf("struct2map 得到的 map 内容为:%v", data)
}
阅读全文