Golang新手请教:关于text/template的两个问题

Golang新手请教:关于text/template的两个问题 大家好,

我是Go语言的新手,如有不当之处还请见谅。我在网上看了一些示例,但遇到了一些我不理解的地方。

我将在此粘贴代码,以及之后的结果:

package main

import (
	"fmt"
	"os"
	"text/template"
)

type Inventory struct {
	Material string
	Count    uint
	Price    float32
}

func main() {
	sweaters := Inventory{"wool", 17, 19.20}
	shoes := Inventory{"leather", 5, 11.35}

	tmpl, err := template.New("test").Parse("{{.Count}} items of {{.Material}}, price {{.Price}}")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("Sweaters:", sweaters)
	fmt.Println("Shoes:", shoes)
	err = tmpl.Execute(os.Stdout, sweaters)
	if err != nil {
		fmt.Println(err)
	}
	err = tmpl.Execute(os.Stdout, shoes)
	if err != nil {
		fmt.Println(err)
	}
}

/usr/local/go/bin/go build [/Users/jre/go/src/jre/template1] Success: process exited with code 0. /Users/jre/go/src/jre/template1/template1 [/Users/jre/go/src/jre/template1] Sweaters: {wool 17 19.2} Shoes: {leather 5 11.35} 17 items of wool, price 19.25 items of leather, price 11.35Success: process exited with code 0.

我的两个问题:

  1. 如何使用 text/template 将浮点数值打印为两位小数?我想这在文档的某个地方有说明,但我还没有看到。
  2. 你可以看到,当我直接打印 sweaters 结构体时,它显示 19.2,但使用 text/template 时却显示为 19.25。通过反复试验,我发现它从 shoes 结构体中获取了第二位小数。我的问题是,它为什么要这样做?我知道这不是编写此代码的最佳方式,我应该将其放入循环中,但有趣的是,第二位小数竟然被改变了。

非常感谢。


更多关于Golang新手请教:关于text/template的两个问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

19.20 等于 19.2。尾随的零会被省略,除非你使用 printf 指令明确要求保留它们。

更多关于Golang新手请教:关于text/template的两个问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@MrJohn

你可以在 text/template 中使用 printf 函数,它提供了类似于 fmt 包中的格式化指令:

{{printf "%9.2f" .Price}}

问题在于你的两个模板之间没有换行符分隔,所以它打印出来的是:

"17 items of wool, price 19.2" + "5 items of leather, price 11.35" = "17 items of wool, price 19.25 items of leather, price 11.35"

你好,肖恩,

感谢你的 printf 解释。我完全理解了。

我理解你关于模板行的回复,但对我来说,仍然感到好奇的是,只有模板“占位符”的一部分被覆盖了。如果 Price 的全部内容都被覆盖,或者完全没有被覆盖,这对我来说是合理的。但偏偏只有一个数字被覆盖了。根据我所看到的,我推测 0 被移除了,所以毛衣的 Price 值是 19.2 而不是 19.20。然而……如果我将值设置为 19.21,我看到打印出来的是 19.215,这就更奇怪了。对此有什么想法吗?

如前所述,我知道这不是编写这个示例的最佳方式。例如,我会将其放入一个循环中,以便独立处理毛衣和鞋子。

非常感谢你的快速回复。

非常感谢,Sean。

我现在已经修改了代码,让模板在循环中运行,并使用printf来格式化价格。它的运行结果符合我的预期:

package main

import (
	"fmt"
	"os"
	"text/template"
)

type Inventory struct {
	Material string
	Count    uint
	Price    float32
}

func main() {
	sweaters := Inventory{"wool", 17, 19.21}
	shoes := Inventory{"leather", 5, 11.35}

	items := []Inventory{}
	items = append(items, sweaters)
	items = append(items, shoes)
	for _, item := range items {
		//fmt.Println(item)

		tmpl, err := template.New("test").Parse("{{.Count}} items of {{.Material}}, price {{printf \"%3.2f\" .Price}}\n")
		if err != nil {
			fmt.Println(err)
		}

		err = tmpl.Execute(os.Stdout, item)
		if err != nil {
			fmt.Println(err)
		}

	}
}

针对你的两个问题,这里提供具体的解决方案和解释:

问题1:格式化浮点数为两位小数

使用printf函数进行格式化:

tmpl, err := template.New("test").Parse("{{.Count}} items of {{.Material}}, price {{printf \"%.2f\" .Price}}")

完整示例:

package main

import (
	"os"
	"text/template"
)

type Inventory struct {
	Material string
	Count    uint
	Price    float32
}

func main() {
	sweaters := Inventory{"wool", 17, 19.20}
	shoes := Inventory{"leather", 5, 11.35}

	tmpl, err := template.New("test").Parse("{{.Count}} items of {{.Material}}, price {{printf \"%.2f\" .Price}}")
	if err != nil {
		panic(err)
	}

	tmpl.Execute(os.Stdout, sweaters)
	os.Stdout.WriteString("\n")
	tmpl.Execute(os.Stdout, shoes)
}

输出:

17 items of wool, price 19.20
5 items of leather, price 11.35

问题2:浮点数精度问题的原因

这个问题是由于text/template在执行时重用缓冲区导致的。当你连续调用Execute时,模板引擎会复用内部缓冲区,而浮点数转换可能没有完全清除之前的残留数据。

解决方案是每次执行后重置输出或使用不同的方式处理输出:

package main

import (
	"bytes"
	"fmt"
	"text/template"
)

type Inventory struct {
	Material string
	Count    uint
	Price    float32
}

func main() {
	sweaters := Inventory{"wool", 17, 19.20}
	shoes := Inventory{"leather", 5, 11.35}

	tmpl, err := template.New("test").Parse("{{.Count}} items of {{.Material}}, price {{.Price}}")
	if err != nil {
		panic(err)
	}

	// 使用独立的缓冲区
	var buf1 bytes.Buffer
	tmpl.Execute(&buf1, sweaters)
	fmt.Println(buf1.String())

	var buf2 bytes.Buffer
	tmpl.Execute(&buf2, shoes)
	fmt.Println(buf2.String())
}

或者使用循环并确保每次都有换行分隔:

func main() {
	sweaters := Inventory{"wool", 17, 19.20}
	shoes := Inventory{"leather", 5, 11.35}
	
	tmpl, err := template.New("test").Parse("{{.Count}} items of {{.Material}}, price {{.Price}}\n")
	if err != nil {
		panic(err)
	}

	items := []Inventory{sweaters, shoes}
	for _, item := range items {
		tmpl.Execute(os.Stdout, item)
	}
}

这样就能确保每个输出都是独立的,避免缓冲区复用导致的精度问题。

回到顶部