上一节的例子中使用了Printf做了输出格式化,当然,对于简单的例子来说足够了,但是我们有时候还是需要复杂的输出格式,甚至需要将格式化代码分离开来。这时,可以使用text/template和html/template。
一个模版就是一个字符串或者一个文件,里面包含了一个或多个{ {..}}形式的对象,这种对象被称为模版的actions。除了actions外,模版的其它部分就按字面值打印,但是对于actions,将触发相应的行为。每个action都是一个模版表达式,模版表达式包含:选择struct中的字段、调用函数或方法、if-else控制语句和range循环语句等等。下面是一个简单的模版:
gopl.io/ch4/issuesreport
const templ = `{
{.TotalCount}} issues:
{
{range .Items}}----------------------------------------
Number: {
{.Number}}
User: {
{.User.Login}}
Title: {
{.Title | printf "%.64s"}}
Age: {
{.CreatedAt | daysAgo}} days
{
{end}}`
让我们继续上节中获取github上的issuses项目。上面这个模版先打印匹配到的issues总数,然后打印每个issue的编号、用户、标题以及存在的时间。对于每一个action,都可以用点操作符"."代表当前的值。点操作符首先初始化为模版的参数(模版初始化时会传入参数),当前例子中对应github.IssuesSearchResult,{ {.TotalCount}} action将被struct中的TotalCount字段值代替并打印。{ {range .Items}}和{ {end}}这两个action合在一起对应一个循环,循环中的点操作符代表的是当前的Items元素值,因此如果循环n次,那么就会打印n次Item中的元素。
在action中, | 操作符表示将前一个表达式的结果用作后一个函数的输入,这个类似于Unix管道。在Title这一行的action中,第二个表达式是printf函数,这个函数是基于fmt.Sprintf实现的内置函数,所有模版都可以直接使用。对于Age这一行,| 后面是一个daysAgo函数,该函数通过time.Since将CreatedAt转换为时间长度:
func daysAgo(t time.Time) int {
return int(time.Since(t).Hours() /