Golang实践录:我的工具包

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

简单介绍

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

模块介绍

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

字符串和数值转换

字符串转为数值:

// Convert string to specify type.
type StrTo string

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

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

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

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

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

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

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

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

func (f StrTo) String() string {
	if f.Exist() {
		return string(f)
	}
	return ""
}

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

数值转换为字符串:


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

十六进制和字符串转换:

// HexStr2int converts hex format string to decimal number.
func HexStr2int(hexStr string) (int) {
	num := 0
	length := len(hexStr)
	for i := 0; i < length; i++ {
		char := hexStr[length-i-1]
		factor := -1

		switch {
		case char >= '0' && char <= '9':
			factor = int(char) - '0'
		case char >= 'a' && char <= 'f':
			factor = int(char) - 'a' + 10
		default:
			return -1
		}

		num += factor * PowInt(16, i)
	}
	return num
}

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

	for num > 0 {
		r := num % 16

		c := "?"
		if r >= 0 && r <= 9 {
			c = string(r + '0')
		} else {
			c = string(r + 'a' - 10)
		}
		hex = c + hex
		num = num / 16
	}
	return hex
}

缓冲区、结构体打印

缓冲区打印:

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的打印:

// 将数组、map等,按行打印,默认fmt.Println是一行
func PrintByLine(w io.Writer, data interface{}) {
	if w == os.Stderr {
		fmt.Fprintf(os.Stderr, "error: ")
	}
	t := reflect.TypeOf(data)

	v := reflect.ValueOf(data)
	if v.Len() == 0 {
		return
	}
	fmt.Fprintf(w, "[\n")
	switch t.Kind() {
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			fmt.Fprintf(w, "%d  %v\n", i+1, v.Index(i))
		}
	case reflect.Map:
		iter := v.MapRange()
		i := 0
		for iter.Next() {
			fmt.Fprintf(w, "%d %v: %v\n", i+1, iter.Key(), iter.Value())
			i += 1
		}
	default:
		fmt.Fprintf(w, "%v\n", data)
	}
	fmt.Fprintf(w, "]\ntotal: %d\n", v.Len())
}

时间相关

// Format unix time int64 to string
func Date(ti int64, format string) string {
	t := time.Unix(int64(ti), 0)
	return DateT(t, format)
}

// Format unix time string to string
func DateS(ts string, format string) string {
	i, _ := strconv.ParseInt(ts, 10, 64)
	return 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 {
	res := strings.Replace(format, "MM", t.Format("01"), -1)
	res = strings.Replace(res, "M", t.Format("1"), -1)
	res = strings.Replace(res, "DD", t.Format("02"), -1)
	res = strings.Replace(res, "D", t.Format("2"), -1)
	res = strings.Replace(res, "YYYY", t.Format("2006"), -1)
	res = strings.Replace(res, "YY", t.Format("06"), -1)
	res = strings.Replace(res, "HH", fmt.Sprintf("%02d", t.Hour()), -1)
	res = strings.Replace(res, "H", fmt.Sprintf("%d", t.Hour()), -1)
	res = strings.Replace(res, "hh", t.Format("03"), -1)
	res = strings.Replace(res, "h", t.Format("3"), -1)
	res = strings.Replace(res, "mm", t.Format("04"), -1)
	res = strings.Replace(res, "m", t.Format("4"), -1)
	res = strings.Replace(res, "ss", t.Format("05"), -1)
	res = strings.Replace(res, "s", t.Format("5"), -1)
	return res
}

延时函数:

func Sleep(ms time.Duration) {
	time.Sleep(ms*time.Millisecond);
}

其它工具包

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

日志

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

Fatal Fatalf Fatalln
Error Errorf Errorln
Warning Warningf Warningln
Exit Exitf Exitln

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

Print Printf Println

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

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

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

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

详情可参考klog源码

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

源码

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

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页