返回

从源码学习 Go 标准库(二):fmt - print(3)

后端

您好,我是喜欢研究编程语言实现的code秘密,很高兴能在这里分享我的研究成果。今天,我将继续带着大家一起阅读Go语言的源码,解读fmt包中print函数的实现。我们将在上篇文章的基础上,继续分析doPrintf函数中宽度和精度的处理部分,并深入研究函数调用的更深层细节。

在上一篇文章中,我们已经梳理了不同打印函数的输出格式,复习了显式参数索引的相关知识,并且分析了函数调用过程中的前两层。现在,我们继续往下看。

// skip leading spaces
for ; i < argIndex && strings.HasPrefix(flags, " "); i++ {
}
// 确定 width,设置 flag
if i < argIndex && flags[i] == '-' {
	flag -= FlagLeft
	i++
}
// 确定 width
if i < argIndex && flags[i] >= '0' && flags[i] <= '9' {
	width = atoi(flags[i:])
	if width == 0 {
		width = -1
	}
	i += len(strconv.Itoa(width))
}
// 确定 precision
if i < argIndex && flags[i] == '.' {
	i++
	prec1 := 0
	if i < argIndex && flags[i] >= '0' && flags[i] <= '9' {
		prec1 = atoi(flags[i:])
		i += len(strconv.Itoa(prec1))
	} else {
		prec1 = -1
	}
	prec = strconv.Itoa(prec1)
}

在这一段代码中,我们首先跳过任何位于参数索引前面的空格,然后检查下一个字符是否是减号(-)。如果是,我们就将flag减去FlagLeft,并跳过下一个字符。接下来,我们检查下一个字符是否是数字,如果是,我们就解析它作为宽度(width),并跳过相应的字符。

接下来,我们检查下一个字符是否是句点(.)。如果是,我们就跳过下一个字符,然后检查下一个字符是否是数字。如果是,我们就解析它作为精度(precision),并跳过相应的字符。如果不是,我们就将精度设置为-1。

现在,我们已经解析了宽度和精度,接下来我们就可以调用fmtFprintf函数来格式化我们的参数。

fmtFprintf(w, format, val...)

fmtFprintf函数与fmt.Fprintf函数非常相似,但是它允许我们直接传入要格式化的参数,而不是使用fmt.Sprint函数将其转换为字符串。

在调用fmtFprintf函数之后,我们就完成了对参数的格式化,接下来我们就需要将格式化的结果输出到目标位置。

if fill == '0' && flag&FlagLeft == 0 {
	padchar := ' '
	if flag&FlagSharp != 0 {
		padchar = '0'
	}
	for i = width - len(s); i > 0; i-- {
		w.WriteByte(padchar)
	}
}
w.WriteString(s)
if fill == '-' || flag&FlagLeft != 0 {
	for i = width - len(s); i > 0; i-- {
		w.WriteByte(' ')
	}
}

在这一段代码中,我们首先检查填充字符(fill)和标志(flag)是否满足某些条件。如果满足,我们就使用WriteByte函数将填充字符输出到目标位置。接下来,我们使用WriteString函数将格式化的字符串输出到目标位置。最后,如果填充字符或标志满足某些条件,我们就使用WriteByte函数将空格输出到目标位置。

以上就是对Go语言源码中fmt包的print函数的分析。希望这篇文章对您有所帮助。