zoukankan      html  css  js  c++  java
  • Go fmt包

    fmt包简介

    我们经常会使用 fmt 包,但是却没有思考过它的实现。我们会在这里使用一个 fmt.Printf,又会在那里使用一个 fmt.Sprintf。但是,如果你仔细研究下这个包,你会发现很多有趣有用的东西。

    由于 Go 在大多数情况下会用来编写服务器或服务程序,我们主要的调试工具就是日志系统。log 包提供的 log.Printf 函数有和 fmt.Printf 相同的语义。 良好且信息丰富的日志消息非常重要,并且如果为你的数据结构添加一些格式化的支持将会为你的日志消息增加有价值的信息。

    格式化输出

    Go 的 fmt 相关的函数支持一些占位符,最常见的是字符串占位符的 %s,整型占位符 %d,以及浮点型占位符 %f。现在让我们探究一些其他的占位符。

    %v & %T

    %v 占位符可以打印任何 Go 的值,%T 可以打印出变量的类型。我经常使用这些占位符来调试程序。

    Copy
    var e interface{} = 2.7182
    fmt.Printf("e = %v (%T)
    ", e, e) // e = 2.7182 (float64)
    

    宽度

    你可以为一个打印的数值指定宽段,比如:

    Copy
    fmt.Printf("%10d
    ", 353)  // will print "       353"
    

    你还可以通过将宽度指定为 * 来将宽度当作 Printf 的参数,例如:

    Copy
    fmt.Printf("%*d
    ", 10, 353)  // will print "       353"
    

    当你打印出数字列表而且希望它们能够靠右对齐时,这非常的有用。

    Copy
    // alignSize return the required size for aligning all numbers in nums
    func alignSize(nums []int) int {
        size := 0
        for _, n := range nums {
            if s := int(math.Log10(float64(n))) + 1; s > size {
                size = s
            }
        }
    
        return size
    }
    
    func main() {
        nums := []int{12, 237, 3878, 3}
        size := alignSize(nums)
        for i, n := range nums {
            fmt.Printf("%02d %*d
    ", i, size, n)
        }
    }
    

    将会打印出:

    Copy
    00   12
    01  237
    02 3878
    03    3
    

    这使得我们更加容易比较数字。

    通过位置引用

    如果你在一个格式化的字符串中多次引用一个变量,你可以使用 %[n],其中 n 是你的参数索引(位置,从 1 开始)。

    Copy
    fmt.Printf("The price of %[1]s was $%[2]d. $%[2]d! imagine that.
    ", "carrot", 23)
    

    这将会打印出:

    Copy
    The price of carrot was $23. $23! imagine that.
    

    %v

    %v 占位符将会打印出 Go 的值,如果此占位符以 + 作为前缀,将会打印出结构体的字段名,如果以 # 作为前缀,那么它会打印出结构体的字段名和类型。

    Copy
    // Point is a 2D point
    type Point struct {
        X int
        Y int
    }
    
    func main() {
        p := &Point{1, 2}
        fmt.Printf("%v %+v %#v 
    ", p, p, p)
    }
    

    这将会打印:

    Copy
    &{1 2} &{X:1 Y:2} &main.Point{X:1, Y:2}
    

    我经常会使用 %+v 这种占位符。

    fmt.Stringer & fmt.Formatter

    有时候你希望能够精细化地控制你的对象如何被打印。例如,当向用户展示错误时,你可能需要的是一个字符串的表示,而当向日志系统写入时,你则希望是更加详细的字符串表示。

    为了控制你的对象如何被打印,你可以实现 fmt.Formatter 接口,也可以选择实现 fmt.Stringer 接口。

    使用 fmt.Formatter 接口比较好的例子是 github.com/pkg/errors 这个非常棒的库。假设你需要加载我们的配置文件,但是你有一个错误。你可以向用户打印一个简短的错误(又或者在 API 中返回),并在日志中输出更加详细的错误。

    Copy
    cfg, err := loadConfig("/no/such/config.toml")
    if err != nil {
        fmt.Printf("error: %s
    ", err)
        log.Printf("can't load config
    %+v", err)
    }
    

    这就会向用户展示:

    Copy
    error: can't open config file: open /no/such/file.toml: no such file or directory
    

    并且,日志文件中会这么记录:

    Copy
    2018/11/28 10:43:00 can't load config
    open /no/such/file.toml: no such file or directory
    can't open config file
    main.loadConfig
        /home/miki/Projects/gopheracademy-web/content/advent-2018/fmt.go:101
    main.main
        /home/miki/Projects/gopheracademy-web/content/advent-2018/fmt.go:135
    runtime.main
        /usr/lib/go/src/runtime/proc.go:201
    runtime.goexit
        /usr/lib/go/src/runtime/asm_amd64.s:1333
    

    下面是一个小例子。假设你有一个 AuthInfo 结构体。

    Copy
    // AuthInfo is authentication information
    type AuthInfo struct {
        Login  string // Login user
        ACL    uint   // ACL bitmask
        APIKey string // API key
    }
    

    你希望此结构被打印时能够隐藏 APIKey 的值。你可以使用 ****** 来代替这个 APIKey 的值。

    首先我们通过 fmt.Stringer 实现。

    Copy
    // String implements Stringer interface
    func (ai *AuthInfo) String() string {
        key := ai.APIKey
        if key != "" {
            key = keyMask
        }
        return fmt.Sprintf("Login:%s, ACL:%08b, APIKey: %s", ai.Login, ai.ACL, key)
    }
    

    现在 fmt.Formatter 获取到了占位符的 fmt.State 和符文。fmt.State 实现了 io.Writer 接口,使得你可以直接写入它。

    想要了解结构体中所有的可用字段,可以使用 reflect 包。这将确保你的代码即使在 AuthInfo 字段更改后也能正常工作。

    Copy
    var authInfoFields []string
    
    func INIt() {
        typ := reflect.TypeOf(AuthInfo{})
        authInfoFields = make([]string, typ.NumField())
        for i := 0; i < typ.NumField(); i++ {
            authInfoFields[i] = typ.Field(i).Name
        }
        sort.Strings(authInfoFields) // People are better with sorted data
    }
    

    现在你就可以实现 fmt.Formatter 接口了。

    Copy
    // Format implements fmt.Formatter
    func (ai *AuthInfo) Format(state fmt.State, verb rune) {
        switch verb {
        case 's', 'q':
            val := ai.String()
            if verb == 'q' {
                val = fmt.Sprintf("%q", val)
            }
            fmt.Fprint(state, val)
        case 'v':
            if state.Flag('#') {
                // Emit type before
                fmt.Fprintf(state, "%T", ai)
            }
            fmt.Fprint(state, "{")
            val := reflect.ValueOf(*ai)
            for i, name := range authInfoFields {
                if state.Flag('#') || state.Flag('+') {
                    fmt.Fprintf(state, "%s:", name)
                }
                fld := val.FieldByName(name)
                if name == "APIKey" && fld.Len() > 0 {
                    fmt.Fprint(state, keyMask)
                } else {
                    fmt.Fprint(state, fld)
                }
                if i < len(authInfoFields)-1 {
                    fmt.Fprint(state, " ")
                }
            }
            fmt.Fprint(state, "}")
        }
    }
    

    现在让我们来试试看结果:

    Copy
    ai := &AuthInfo{
        Login:  "daffy",
        ACL:    ReadACL | WriteACL,
        APIKey: "duck season",
    }
    fmt.Println(ai.String())
    fmt.Printf("ai %%s: %s
    ", ai)
    fmt.Printf("ai %%q: %q
    ", ai)
    fmt.Printf("ai %%v: %v
    ", ai)
    fmt.Printf("ai %%+v: %+v
    ", ai)
    fmt.Printf("ai %%#v: %#v
    ", ai)
    

    这样这样打印出:

    Copy
    Login:daffy, ACL:00000011, APIKey: *****
    ai %s: Login:daffy, ACL:00000011, APIKey: *****
    ai %q: "Login:daffy, ACL:00000011, APIKey: *****"
    ai %v: {3 ***** daffy}
    ai %+v: {ACL:3 APIKey:***** Login:daffy}
    ai %#v: *main.AuthInfo{ACL:3 APIKey:***** Login:daffy}
    
    每天逼着自己写点东西,终有一天会为自己的变化感动的。这是一个潜移默化的过程,每天坚持编编故事,自己不知不觉就会拥有故事人物的特质的。 Explicit is better than implicit.(清楚优于含糊)
  • 相关阅读:
    JavaScript 知识
    Sleep,Hibernate and Hybrid
    CentOS 7 休眠系统
    centos7如何添加开机启动服务/脚本
    linux下执行.sh文件的方法和语法
    systemctl命令
    linux 更新yum源 改成阿里云源
    Supervisor进程管理&开机自启
    Django中的Request和Response
    ORACLE 触发器
  • 原文地址:https://www.cnblogs.com/kylin5201314/p/14945246.html
Copyright © 2011-2022 走看看