go json 自定义_[系列]go源码解读,fmt —— 接口介绍

前言

fmt包实现了类似C语言printf和scanf的格式化I/O。格式化动作('verb')源自C语言但更简单。本文将介绍 Stringer, GoStringer, State, Formatter这几个接口的作用。

Stringer 接口

type Stringer interface {
    String() string
}

如果一个对象实现了这个Stringer接口,那么通过%s,%v打印,这时候会判断这个对象是否实现了Stringer接口,如果实现了,就调用对象的String方法,怎么感觉有点像python中的 __str__ 方法。

package main

import "fmt"

type person struct {
    Name string
    age int
}

func (p *person) String() string {
    return p.Name
}

func main() {
    p := &person{"jack",22}

    println(p)           // 0xc000090000,这个打印的是地址
    fmt.Println(p)       // jack
    fmt.Printf("%vn",p) // jack
    fmt.Printf("%sn",p) // jack
}
对于内建函数 println() , print()是不能调用到 String方法的。
对于某些结构体,当某些字段不希望在打印的时候输出时,可以实现 Stringer接口,达到隐藏某些字段的目的。

GoStringer 接口

type GoStringer interface {
    GoString() string
}

实现了GoStringer接口的对象,当采用 %#v格式化输出时(其他的形式会不会呢?),会调用GoString方法来生成输出定义了该类型值的go语法表示。就是说这个是go特有的,比如&main.person{Name:"jack", age:22} 这种样式的就是go语法来表示一个对象,当然我们可以自己定义。

package main

import (
    "encoding/json"
    "fmt"
)

type person struct {
    Name string
    age int
}

func (p *person) GoString() string {
    b, _ :=json.Marshal(p)
    return string(b)
}

func main() {
    p := &person{"jack",22}

    fmt.Printf("%vn",p)    // &{jack 22}
    fmt.Printf("%#vn",p)   // {"Name":"jack"} , GoString的输出
    fmt.Printf("%+vn",p)   // &{Name:jack age:22}
}
这里我们通过实现 GoStringer接口,并返回对象 json 序列化后的字符串来达到改写对象的go语法表示。

State 接口

type State interface {
    // Write方法用来写入格式化的文本
    Write(b []byte) (ret int, err error)
    // Width返回宽度值,及其是否被设置
    Width() (wid int, ok bool)
    // Precision返回精度值,及其是否被设置
    Precision() (prec int, ok bool)
    // Flag报告是否设置了flag c(一个字符,如+、-、#等)
    Flag(c int) bool
}

在学习 State 接口之前,我们先来看一下,go的格式化语法

6add52a42a5ce991ca6469ae2dd09cc4.png
% 表示格式化的开始位置 % 后面紧跟的 +号,表示 flags + 后面的 3.2表示宽度 Width和精度 Precision f 为占位符 verb

那么go格式化语法包括了 开始位置flags宽度精度占位符 五个部分组成,State接口包含了其中三项信息。在解析格式化字符串的时候,会一个一个解析,当解析完成一个这样的%+3.2f整体结构以后,数据保存到State,下一步调用printArg进行格式化,并将格式化后的数据写入buffer,所有的结构解析完成之后写入到io.Writer,并在控制台输出(后面会具体讨论代码是怎么实现的)。

Formatter 接口

type Formatter interface {
    Format(f State, c rune)
}

Formatter 用于实现自定义格式化方法。可通过在自定义结构体中实现 Format 方法来实现这个目的。标准库中使用在 /usr/local/go/src/fmt/print.go:580,会首先判断结构体是否实现了 Formatter 接口,实现了则利用自定义 Formatter 格式化参数。未实现,则最大程度的利用 go语法格式 GoStringer 默认规则去格式化参数,其次是 errorStringer 接口。

我们来看一下大厂 B站是怎么实现这个接口的

// Format is a support routine for fmt.Formatter. It accepts the decimal
// formats 'd' and 'f', and handles both equivalently.
// Width, precision, flags and bases 2, 8, 16 are not supported.
func (x *Dec) Format(s fmt.State, ch rune) {
    if ch != 'd' && ch != 'f' && ch != 'v' && ch != 's' {
        fmt.Fprintf(s, "%%!%c(dec.Dec=%s)", ch, x.String())
        return
    }
    fmt.Fprintf(s, x.String())
}

是不是很简单?

小结

本小节介绍了fmt包中的几个基础接口和使用。下一篇,我们将深入fmt包的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值