Golang实践录:我的工具包

程序员难免会自造轮子,因为有时候自己的轮子才更适合自己,golang 的生态圈不错,官方的,非官方都有很多不同功能的库。本文从小处着眼,基于 github 开源工程创建属于自己的工具包。

简单介绍

本文的工具包,不依赖第三方库,全部使用官方的包。实际中使用了第三方库,则另起目录,作为其它包提供。不影响本包。本包命名为 com,可理解为通用的包。包括但不限于以下内容:
数值和字符串转换,进制转换。
目录、文件操作。
日期、时间。
命令执行。
MD5、base64。
地址内容打印Dump。

模块介绍

本小节列出一些函数的实现,详细参考文后源码地址。

字符串和数值转换

字符串转为数值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Convert string to specify type.
type StrTo string

func (f StrTo) Exist() bool {
truereturn string(f) != string(0x1E)
}

func (f StrTo) Uint8() (uint8) {
truev, _ := strconv.ParseUint(f.String(), 10, 8)
truereturn uint8(v)
}

func (f StrTo) Int() (int) {
truev, _ := strconv.ParseInt(f.String(), 10, 0)
truereturn int(v)
}

func (f StrTo) Int64() (int64) {
truev, _ := strconv.ParseInt(f.String(), 10, 64)
truereturn int64(v)
}

func (f StrTo) Float64() (float64) {
truev, _ := strconv.ParseFloat(f.String(), 64)
truereturn float64(v)
}

func (f StrTo) Uint8Hex() (uint8) {
truev, _ := strconv.ParseUint(f.String(), 16, 8)
truereturn uint8(v)
}

func (f StrTo) IntHex() (int) {
truev, _ := strconv.ParseInt(f.String(), 16, 0)
truereturn int(v)
}

func (f StrTo) Int64Hex() (int64) {
truev, _ := strconv.ParseInt(f.String(), 16, 64)
truereturn int64(v)
}

func (f StrTo) String() string {
trueif f.Exist() {
truetruereturn string(f)
true}
truereturn ""
}

可根据不同函数,将字符串转换成对应的数值。注意,此处不判断原始字符串,通过不同函数指定格式。如256,即可认为十进制,也可认为是十六进制。

数值转换为字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

// Convert any type to string.
func ToStr(value interface{}, args ...int) (s string) {
trueswitch v := value.(type) {
truecase bool:
truetrues = strconv.FormatBool(v)
truecase float32:
truetrues = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32))
truecase float64:
truetrues = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64))
truecase int:
truetrues = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
truecase int8:
truetrues = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
truecase int16:
truetrues = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
truecase int32:
truetrues = strconv.FormatInt(int64(v), argInt(args).Get(0, 10))
truecase int64:
truetrues = strconv.FormatInt(v, argInt(args).Get(0, 10))
truecase uint:
truetrues = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
truecase uint8:
truetrues = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
truecase uint16:
truetrues = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
truecase uint32:
truetrues = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10))
truecase uint64:
truetrues = strconv.FormatUint(v, argInt(args).Get(0, 10))
truecase string:
truetrues = v
truecase []byte:
truetrues = string(v)
truedefault:
truetrues = fmt.Sprintf("%v", v)
true}
truereturn s
}

十六进制和字符串转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// HexStr2int converts hex format string to decimal number.
func HexStr2int(hexStr string) (int) {
truenum := 0
truelength := len(hexStr)
truefor i := 0; i < length; i++ {
truetruechar := hexStr[length-i-1]
truetruefactor := -1

truetrueswitch {
truetruecase char >= '0' && char <= '9':
truetruetruefactor = int(char) - '0'
truetruecase char >= 'a' && char <= 'f':
truetruetruefactor = int(char) - 'a' + 10
truetruedefault:
truetruetruereturn -1
truetrue}

truetruenum += factor * PowInt(16, i)
true}
truereturn num
}

// Int2HexStr converts decimal number to hex format string.
func Int2HexStr(num int) (hex string) {
trueif num == 0 {
truetruereturn "0"
true}

truefor num > 0 {
truetruer := num % 16

truetruec := "?"
truetrueif r >= 0 && r <= 9 {
truetruetruec = string(r + '0')
truetrue} else {
truetruetruec = string(r + 'a' - 10)
truetrue}
truetruehex = c + hex
truetruenum = num / 16
true}
truereturn hex
}

缓冲区、结构体打印

缓冲区打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func Dump(by []byte, len int) {
line := 16
n := len / line
if len % line != 0 {
n++
}
for i := 0; i < n; i++ {
fmt.Printf("%08x ", i*line)
for j := 0; j < line; j++ {
if i*line+j < len {
fmt.Printf("%02x ", by[i*line+j])
} else {
fmt.Printf(" ")
}
if j == 7 {
fmt.Printf(" ")
}
}
fmt.Printf(" |")
for j := 0; j<line && (i*line+j)<len; j++ {
if (i*line+j) < len {
c := by[i*line+j]
if c >= ' ' && c < '~'{
fmt.Printf("%c", c)
} else {
fmt.Printf(".")
}
} else {
fmt.Printf(" ")
}
}
fmt.Printf("|\n")
}
}

该函数实际为笔者 C 语言版本的改写。

结构体或map的打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 将数组、map等,按行打印,默认fmt.Println是一行
func PrintByLine(w io.Writer, data interface{}) {
trueif w == os.Stderr {
truetruefmt.Fprintf(os.Stderr, "error: ")
true}
truet := reflect.TypeOf(data)

truev := reflect.ValueOf(data)
trueif v.Len() == 0 {
truetruereturn
true}
truefmt.Fprintf(w, "[\n")
trueswitch t.Kind() {
truecase reflect.Slice, reflect.Array:
truetruefor i := 0; i < v.Len(); i++ {
truetruetruefmt.Fprintf(w, "%d %v\n", i+1, v.Index(i))
truetrue}
truecase reflect.Map:
truetrueiter := v.MapRange()
truetruei := 0
truetruefor iter.Next() {
truetruetruefmt.Fprintf(w, "%d %v: %v\n", i+1, iter.Key(), iter.Value())
truetruetruei += 1
truetrue}
truedefault:
truetruefmt.Fprintf(w, "%v\n", data)
true}
truefmt.Fprintf(w, "]\ntotal: %d\n", v.Len())
}

时间相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Format unix time int64 to string
func Date(ti int64, format string) string {
truet := time.Unix(int64(ti), 0)
truereturn DateT(t, format)
}

// Format unix time string to string
func DateS(ts string, format string) string {
truei, _ := strconv.ParseInt(ts, 10, 64)
truereturn Date(i, format)
}

// Format time.Time struct to string
// MM - month - 01
// M - month - 1, single bit
// DD - day - 02
// D - day 2
// YYYY - year - 2006
// YY - year - 06
// HH - 24 hours - 03
// H - 24 hours - 3
// hh - 12 hours - 03
// h - 12 hours - 3
// mm - minute - 04
// m - minute - 4
// ss - second - 05
// s - second = 5
// TODO ms
func DateT(t time.Time, format string) string {
trueres := strings.Replace(format, "MM", t.Format("01"), -1)
trueres = strings.Replace(res, "M", t.Format("1"), -1)
trueres = strings.Replace(res, "DD", t.Format("02"), -1)
trueres = strings.Replace(res, "D", t.Format("2"), -1)
trueres = strings.Replace(res, "YYYY", t.Format("2006"), -1)
trueres = strings.Replace(res, "YY", t.Format("06"), -1)
trueres = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
trueres = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
trueres = strings.Replace(res, "hh", t.Format("03"), -1)
trueres = strings.Replace(res, "h", t.Format("3"), -1)
trueres = strings.Replace(res, "mm", t.Format("04"), -1)
trueres = strings.Replace(res, "m", t.Format("4"), -1)
trueres = strings.Replace(res, "ss", t.Format("05"), -1)
trueres = strings.Replace(res, "s", t.Format("5"), -1)
truereturn res
}

延时函数:

1
2
3
func Sleep(ms time.Duration) {
truetime.Sleep(ms*time.Millisecond);
}

其它工具包

本小节列出其它的工具包。

日志

我一起纠结使用哪个日志库,在犹豫中花费很多时间,最后决定先用着一个版本,待到不合适时,再选择其它的。在接触 KubeEdge项目时,了解了 klog 库,考虑到其轻便,最终改造并使用。为了保持原样,其位置和名称均无变化。原始版本提供的主要接口函数如下:

1
2
3
4
Fatal Fatalf Fatalln
Error Errorf Errorln
Warning Warningf Warningln
Exit Exitf Exitln

为了方便自己理解和使用,额外再添加:

1
Print Printf Println

这样就可以和 fmt 无缝切换了。

另外,考虑到不需要 pid,所以在输出提示符中去掉了 pid。输出格式如下:

1
[2020-10-20 21:47:29.411 busy.go:20] hello world

此提示符为笔者一直使用且已习惯。

详情可参考klog源码

补记:
log4go功能强大,但似乎不能转义\n,会将其原样输出。

源码

本文所述工具包,大部分来自 无闻github 的开源项目,在实践中不断优化并添加自己认为必要的工具函数。详细可参考笔者的 golang工程 。在此向 无闻 致敬!