%v the value in a default format
when printing structs, the plus flag (%+v) adds field names
%#v a Go-syntax representation of the value
%T a Go-syntax representation of the type of the value
%% a literal percent sign; consumes no value
%t the word true or false
Flags
+ always print a sign for numeric values;
guarantee ASCII-only output for %q (%+q)
- pad with spaces on the right rather than the left (left-justify the field)
# alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
0X for hex (%#X); suppress 0x for %p (%#p);
for %q, print a raw (backquoted) string if strconv.CanBackquote
returns true;
always print a decimal point for %e, %E, %f, %F, %g and %G;
do not remove trailing zeros for %g and %G;
write e.g. U+0078 'x' if the character is printable for %U (%#U).
' ' (space) leave a space for elided sign in numbers (% d);
put spaces between bytes printing strings or slices in hex (% x, % X)
0 pad with leading zeros rather than spaces;
for numbers, this moves the padding after the sign
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
p.free()
return
}
与 Print 相比,最大的不同就是 doPrintf 方法了。在这里我们来详细看看其代码,如下:
func (p *pp) doPrintf(format string, a []interface{}) {
end := len(format)
argNum := 0 // we process one argument per non-trivial format
afterIndex := false // previous item in format was an index like [3].
p.reordered = false
formatLoop:
for i := 0; i < end; {
p.goodArgNum = true
lasti := i
for i < end && format[i] != '%' {
i++
}
if i > lasti {
p.buf.WriteString(format[lasti:i])
}
if i >= end {
// done processing format string
break
}
// Process one verb
i++
// Do we have flags?
p.fmt.clearflags()
simpleFormat:
for ; i < end; i++ {
c := format[i]
switch c {
case '#': //'#'、'0'、'+'、'-'、' '
...
default:
if 'a' <= c && c <= 'z' && argNum < len(a) {
...
p.printArg(a[argNum], rune(c))
argNum++
i++
continue formatLoop
}
break simpleFormat
}
}
// Do we have an explicit argument index?
argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
// Do we have width?
if i < end && format[i] == '*' {
...
}
// Do we have precision?
if i+1 < end && format[i] == '.' {
...
}
if !afterIndex {
argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
}
if i >= end {
p.buf.WriteString(noVerbString)
break
}
...
switch {
case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
p.buf.WriteByte('%')
case !p.goodArgNum:
p.badArgNum(verb)
case argNum >= len(a): // No argument left over to print for the current verb.
p.missingArg(verb)
case verb == 'v':
...
fallthrough
default:
p.printArg(a[argNum], verb)
argNum++
}
}
if !p.reordered && argNum < len(a) {
...
}
}
func (p *pp) handleMethods(verb rune) (handled bool) {
if p.erroring {
return
}
// Is it a Formatter?
if formatter, ok := p.arg.(Formatter); ok {
handled = true
defer p.catchPanic(p.arg, verb)
formatter.Format(p, verb)
return
}
// If we're doing Go syntax and the argument knows how to supply it, take care of it now.
...
return false
}
这个方法比较特殊,一般在自定义结构体和未知情况下进行调用。主要流程是:
若当前参数为错误 verb 标识符,则直接返回
判断是否实现了 Formatter
实现,则利用自定义 Formatter 格式化参数
未实现,则最大程度的利用 Go syntax 默认规则去格式化参数
拓展
在 fmt 标准库中可以通过自定义结构体来实现方法的自定义,大致如下几种
fmt.State
type State interface {
Write(b []byte) (n int, err error)
Width() (wid int, ok bool)
Precision() (prec int, ok bool)
Flag(c int) bool
}
State 用于获取标志位的状态值,涉及如下:
Write:将格式化完毕的字符写入缓冲区中,等待下一步处理
Width:返回宽度信息和是否被设置
Precision:返回精度信息和是否被设置
Flag:返回特殊标志符('#'、'0'、'+'、'-'、' ')是否被设置
fmt.Formatter
type Formatter interface {
Format(f State, c rune)
}
Formatter 用于实现自定义格式化方法。可通过在自定义结构体中实现 Format 方法来实现这个目的
另外,可以通过 f 获取到当前标识符的宽度、精度等状态值。c 为 verb 标识符,可以得到其动作是什么