当前位置: 首页 > news >正文

Go-知识-fmt

Go-知识-fmt

  • 介绍
  • 数值类型
  • 字符类型
  • 布尔类型
  • 其他
  • API
    • Fprint
    • Print
    • Sprint
    • Fprintf
    • 格式穷举
    • Printf
    • Sprintf
    • Fprintln
    • Println
    • Sprintln
  • Appendln
  • error
  • 自定义

Go-知识-fmt

介绍

fmt 实现了格式化输出,并提供了相应的占位符。

支持的数据类型如下:

  • 数值类型:整数类型,浮点类型
  • 字符类型
  • 指针类型
  • 布尔类型
  • 其他

数值类型

  • %b : 二进制
  • %o : 八进制
  • %x : 十六进制
  • %X : 十六进制
  • %d : 十进制
  • %f : 浮点类型
  • %e : 科学计数法
  • %E : 科学计数法

试一试

func TestFmt(t *testing.T) {
	number := 100.234
	numberInt := 45
	fmt.Printf("整数%%d \t %d\n", numberInt)
	fmt.Printf("八进制%%o \t %o\n", numberInt)
	fmt.Printf("十六进制%%x \t %x\n", numberInt)
	fmt.Printf("十六进制%%X \t %X\n", numberInt)
	fmt.Printf("布尔值%%b \t %b\n", numberInt)
	fmt.Printf("浮点值%%f \t %f\n", number)
	fmt.Printf("科学计数法%%e \t %e\n", number)
	fmt.Printf("科学计数法%%E \t %E\n", number)
}

执行结果如下

image-20250303200412387

字符类型

  • %s : 字符类型
  • %q : 带双引号

如下代码

func TestFmtString(t *testing.T) {
	str := "hello world"
	fmt.Printf("字符类型%%s \t %s\n", str)
	fmt.Printf("待双引号%%q \t %q\n", str)
}

执行结果如下

image-20250303200717948

布尔类型

  • %t : 布尔类型
func TestFmtBool(t *testing.T) {
	b := true
	fmt.Printf("布尔类型%%b \t %t\n", b)
}

image-20250303200907588

其他

  • %T : 判断类型(输出类型)
  • %p : 指针类型
  • %v : 默认格式
  • %#v : 带语法的格式
func TestFmtOther(t *testing.T) {
	a := 1
	b := 2.0
	ok := true
	ptr := &a
	s := struct {
		Name string
	}{
		Name: "test",
	}
	fmt.Printf("类型%%T \t %T\n", a)
	fmt.Printf("类型%%T \t %T\n", b)
	fmt.Printf("类型%%T \t %T\n", ok)
	fmt.Printf("类型%%T \t %T\n", ptr)
	fmt.Printf("类型%%T \t %T\n", s)
	fmt.Println()
	fmt.Printf("指针%%p \t %p\n", ptr)
	fmt.Printf("指针%%p \t %p\n", &a)
	fmt.Printf("默认格式%%v \t %v\n", s)
	fmt.Printf("带语法格式%%#v \t %#v\n", s)
}

image-20250303201340465

API

  • Fprint/Fprintf/Fprintln : 带格式的输出
  • Print/Printf/Println : 标准输出
  • Sprint/Sprintf/Sprintln : 格式化内容为 string

Fprint/Print/Sprint 表示使用默认的格式输出或者格式化内容,Fprintf/Printf/Sprintf表示使用指定的格式输出或格式化内容,Fprintln/Println/Sprintln 表示使用默认的格式输出或格式化内容,同时会在最后加上换行符\n

Fprint

源码如下:

// 
func Fprint(w io.Writer, a ...any) (n int, err error) {
	p := newPrinter()
	p.doPrint(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}

newPrinter 干了啥

func newPrinter() *pp {
	p := ppFree.Get().(*pp)
	p.panicking = false
	p.erroring = false
	p.wrapErrs = false
	p.fmt.init(&p.buf)
	return p
}

ppFree 又是个啥

var ppFree = sync.Pool{
	New: func() any { return new(pp) },
}

ppFree 是一个缓存池

image-20250303214241000

提高对象的利用率

p := ppFree.Get().(*pp)是从缓存池中拿一个 pp 结构,因为缓存池是 any 类型的,所以需要进行类型强转

Pp 结构如下:

type pp struct {
	buf buffer

	// arg holds the current item, as an interface{}.
	arg any

	// value is used instead of arg for reflect values.
	value reflect.Value

	// fmt is used to format basic items such as integers or strings.
	fmt fmt

	// reordered records whether the format string used argument reordering.
	reordered bool
	// goodArgNum records whether the most recent reordering directive was valid.
	goodArgNum bool
	// panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.
	panicking bool
	// erroring is set when printing an error string to guard against calling handleMethods.
	erroring bool
	// wrapErrs is set when the format string may contain a %w verb.
	wrapErrs bool
	// wrappedErrs records the targets of the %w verb.
	wrappedErrs []int
}

有很多的属性

p.fmt.init(&p.buf)设置pp结构的 fmt 的属性

其中的 fmt 结构

type fmt struct {
	buf *buffer

	fmtFlags

	wid  int // width
	prec int // precision

	// intbuf is large enough to store %b of an int64 with a sign and
	// avoids padding at the end of the struct on 32 bit architectures.
	intbuf [68]byte
}

type fmtFlags struct {
	widPresent  bool
	precPresent bool
	minus       bool
	plus        bool
	sharp       bool
	space       bool
	zero        bool

	// For the formats %+v %#v, we set the plusV/sharpV flags
	// and clear the plus/sharp flags since %+v and %#v are in effect
	// different, flagless formats set at the top level.
	plusV  bool
	sharpV bool
}

对于 pp 结构中 fmt 的初始化

func (f *fmt) clearflags() {
	f.fmtFlags = fmtFlags{}
}

func (f *fmt) init(buf *buffer) {
	f.buf = buf
	f.clearflags()
}

设置了缓存区,同时清空了标志位。因为 pp 的指针值是缓存的,拿出来的可能是之前用过的,所以需要先初始化清空一下才能使用。

p := newPrinter()之后就是 p.doPrint(a) , 看下 doPrint

func (p *pp) doPrint(a []any) {
	prevString := false
	for argNum, arg := range a {
		isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
		// Add a space between two non-string arguments.
		if argNum > 0 && !isString && !prevString {
			p.buf.writeByte(' ')
		}
		p.printArg(arg, 'v')
		prevString = isString
	}
}

先判断是不是字符串,如果不是字符串,那么在每个值中间加一个 空格

接着调用 printArg 打印

printArg就是针对go支持的所有格式化占位符,进行替换的一个过程

image-20250303215141010

等待占位符替换完成后,将缓存区内的数据,写入到传入的writer里,n, err = w.Write(p.buf)

最后一步的 p.free() 是把从缓存池中拿出来的对象还回去,并且刷新和释放缓存区

func (p *pp) free() {
	// Proper usage of a sync.Pool requires each entry to have approximately
	// the same memory cost. To obtain this property when the stored type
	// contains a variably-sized buffer, we add a hard limit on the maximum
	// buffer to place back in the pool. If the buffer is larger than the
	// limit, we drop the buffer and recycle just the printer.
	//
	// See https://golang.org/issue/23199.
	if cap(p.buf) > 64*1024 {
		p.buf = nil
	} else {
		p.buf = p.buf[:0]
	}
	if cap(p.wrappedErrs) > 8 {
		p.wrappedErrs = nil
	}

	p.arg = nil
	p.value = reflect.Value{}
	p.wrappedErrs = p.wrappedErrs[:0]
	ppFree.Put(p)
}

Print

Print 就是调用了 Fprint ,只是传入的writer是标准输出设备

func Print(a ...any) (n int, err error) {
	return Fprint(os.Stdout, a...)
}

Sprint

SprintFprint 差不多,Sprint不需要将缓存区里面的数据写入到 writer了,直接转为字符串返回即可

func Sprint(a ...any) string {
	p := newPrinter()
	p.doPrint(a)
	s := string(p.buf)
	p.free()
	return s
}

Fprintf

Fprintf 相比 Fprint 多了一个入参,也就是格式串。格式串就是带有占位符的字符串

func Fprintf(w io.Writer, format string, a ...any) (n int, err error) {
	p := newPrinter()
	p.doPrintf(format, a)
	n, err = w.Write(p.buf)
	p.free()
	return
}

其主要逻辑与 Fprint 相同,区别在于 doPrintf

image-20250304200410022

通过 for i := 0; i < end; 循环逐字符解析格式字符串,分为两个处理阶段:

  • 普通字符:直接写入缓冲区
  • 格式化指令:以 % 开头的部分

image-20250304200526710

遇到 % 后执行以下步骤:

  • 解析标志(#, 0, +, -, )
  • 处理参数索引(如 %[3]d)
  • 解析宽度和精度(支持 * 动态值)
  • 处理特殊动词:
    case ‘w’:
    p.wrappedErrs = append(p.wrappedErrs, argNum)
    case ‘v’:
    // 处理 Go 语法格式

image-20250304201135672

参数处理

  • 使用 argNum 跟踪当前参数索引
  • 调用 printArg 进行实际格式化操作
  • 支持参数重排序(%[n] 语法)

image-20250304201233577

  • 错误包装:处理 %w 时记录错误参数位置
  • 类型反射:通过 reflect.Value 处理不同类型
  • 性能优化:使用 buffer 结构进行高效字符串拼接
  • 语法兼容:支持完整的 printf 语法规范

格式穷举

格式串输出示例说明
%v42通用格式,自动匹配类型
%+v{Name: ""}带字段名的结构体输出
%#v"go"Go语法表示值(带类型信息)
%Tfloat64输出值的类型
%d255十进制整数
%b101二进制表示
%o10八进制表示
%xf小写十六进制
%XF大写十六进制
%cAUnicode字符
%f3.141500默认精度浮点数
%.2f3.14保留2位小数
%e1.234500e+03科学计数法表示
%g1.23456789e+08自动选择最紧凑表示法
%shello原始字符串输出
%q"go"带双引号的字符串
%x676f字符串的十六进制编码
%5d 42右对齐宽度5
%-5d42 左对齐宽度5
%05d00042零填充宽度5
%+d+42显示正负号
% d 42正数前留空格
%[2]d %[1]d2 1参数索引重排序
%*d 42动态宽度(参数指定宽度5)
%.*f3.14动态精度(参数指定精度2)
%p0xc0000160a8指针地址
%werror错误包装(需配合errors包使用)
%v[1 2]切片/数组的默认输出
%#v[]int{1, 2}切片/数组的Go语法表示
%+#10.3f +3.142组合格式:符号+宽度10+精度3
%[3]d %[1]s42 hello多参数混合索引
%+v{X:1 Y:2}结构体带字段名输出
%UU+0041Unicode码点格式
%#b0b101Go语法二进制表示
%#o0o10Go语法八进制表示
%#x0xfGo语法十六进制表示
%sMyInt自定义类型实现String()方法时的输出
%d%!d(string=text)类型不匹配时的错误提示
%d%!d(MISSING)缺少参数时的错误提示

Printf

Printf 很简单直接用标准输出调用 Fprintf

func Printf(format string, a ...any) (n int, err error) {
	return Fprintf(os.Stdout, format, a...)
}

Sprintf

Sprintf 不需要写入write,直接将缓存区的内容返回即可

func Sprintf(format string, a ...any) string {
	p := newPrinter()
	p.doPrintf(format, a)
	s := string(p.buf)
	p.free()
	return s
}

Fprintln

Fprintln 基本上也大差不差的,核心是 doPrintln

func Fprintln(w io.Writer, a ...any) (n int, err error) {
	p := newPrinter()
	p.doPrintln(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}

doPrintlndoPrint的基础上,在最后加了换行符\n ,都是使用默认格式打印的

func (p *pp) doPrintln(a []any) {
	for argNum, arg := range a {
		if argNum > 0 {
			p.buf.writeByte(' ')
		}
		p.printArg(arg, 'v')
	}
	p.buf.writeByte('\n')
}

Println

Println 将标准输出作为 writer 请求 Fprintln

func Println(a ...any) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}

Sprintln

Sprintln 不需要写入writer 直接将缓冲区的内容返回即可

func Sprintln(a ...any) string {
    p := newPrinter()
    p.doPrintln(a)
    s := string(p.buf)
    p.free()
    return s
}

Appendln

追加换行

func TestAppln(t *testing.T) {
	var s []byte
	s = fmt.Appendln(s, "hello world")
	s = fmt.Appendln(s, "hello world")
	s = fmt.Appendln(s, "hello world")
	fmt.Print(string(s))
}

image-20250304203750503

error

在格式串中 %w 是错误类型的格式串

除此之外,fmt有针对错误的函数Errorf

image-20250304210059103

doPrintf里面会统计 %w的信息

image-20250304210129623

image-20250304210154743

如果只有一个 %w,那么直接使用 errors.New,如果有多个,需要进行warp处理

errors.New 实际上就是 stringError

image-20250304210248207

warpErrors 就是多个错误

image-20250304210322739

自定义

在java等一些语言中,输出调用自动从 Object继承的ToString方法,将对象信息转为字符串。

在go里面也有类似的接口

image-20250304210451478

Stringer 接口定义的String接口就是默认的结构体转字符串的调用方法

type T struct {
	Name string
}

func (t T) String() string {
	return fmt.Sprintln(fmt.Sprintf("%q", t.Name))
}

func TestString(t *testing.T) {
	name := T{"hello world"}
	fmt.Print(name)
}

image-20250304210754399

除此之外,还有一个 接口GoStringer 定义的 GoString 用于适配 %#v 才会调用

image-20250304210921969

type T struct {
	Name string
}

func (t T) String() string {
	return fmt.Sprintln(fmt.Sprintf("%q", t.Name))
}

func (t T) GoString() string {
	return fmt.Sprintln(fmt.Sprintf("struct T :  %q", t.Name))
}

func TestString(t *testing.T) {
	name := T{"hello world"}
	fmt.Print(name)
    fmt.Printf("%#v", name)
}

image-20250304211043872

相关文章:

  • 模型微调-基于LLaMA-Factory进行微调的一个简单案例
  • c#财务软件专业版企业会计做账软件财务管理系统软件
  • 【技术白皮书】外功心法 | 第二部分 | 计算机运行原理(数据是用二进制数表示的)
  • MySQL索引下推
  • 共绘智慧升级,看永洪科技助力由由集团起航智慧征途
  • 买股票的最佳时机 - 1
  • TDengine 服务无法启动常见原因
  • 【2025小黑课堂】计算机二级WPS精选系列20G内容(可下载:真题+预测卷+软件+选择题)
  • Ubuntu虚拟机中使用QEMU搭建ARM64环境
  • 丰田凯美瑞灯光操作教程:详细开关指南
  • 【halcon】如何理解 halcon 中的domain 之 “区域被裁剪掉了!”
  • Javascript 数组
  • 电感类型性能参数对比
  • QGIS 3D地图制作全流程指南
  • LVGL直接解码png图片的方法
  • 【AD】5-13 特殊粘贴使用
  • CentOS Docker 安装指南
  • C 语言异常处理方式全面解析
  • dify通过ollama简单配置deepseek模型
  • Vercel Serverless
  • wordpress增加知识共享协议/aso优化平台
  • 网站域名到期后不续费会怎样/竞价网站
  • 房产经济人怎么做网站/长沙百度
  • jsp网站开发登陆/手机百度如何发布广告
  • 广告设计作品图片/百度搜索关键词排名优化技术
  • 网站建设太金手指六六二九/广东搜索引擎优化