程序员难免会自造轮子,因为有时候自己的轮子才更适合自己,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()) }延时函数:
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工程 。