理解template package之前,最好能快速看一下Go的一些数据结构,比如struct,array 和slice,这些数据结构在template 里面会被经常用到。
Struct
struct是一个包含多个field的集合,跟C语言的结构体非常类似,可以用关键字type以及struct来定义一个struct。例如:
type My_Struct_Name struct { X int Y string }
注意上面的定义的struct在当前这个package外面也是可见的,因为它的名字是以大写字母开头的。变量X和Y同样也是包外面可见的,因为它们都是大写字母开头的。可以通过.+field的名字来访问Struct里面定义的fields,比如:
msn := My_Struct_Name{1,"talim"}
msn.X = 4
若通过变量名可以以任何顺序初始化这个Struct,如下:
msn2 := My_Struct_Name{Y: “Talim”,X: 15}
Array
Go语言的数组是一个包含指定数量元素的数列。
在下面的代码片断中,我们创建一个名字为”arr”的数组,它包含了5个int类型的整数,元素的类型及长度就是数据的类型和长度。默认数组里面元素的值是0。
var arr [5]int
我们可以通过数组的下标来给一个元素赋值,语法是“array[index] = value”,也可以通过下标来取值。
下面的语法中我们可以通过一行代码来声明和初始化一个数组:
arr2 := [5]int{1,2,3,4,5}
Slice
slice是数组的一个segment(片段), 跟数组一样,slice可以通过下标来存取,同时也有长度。跟数组不一样的是,slice的长度是可变的。下面就是一个slice的一个例子:
var x []int
这个跟数组唯一不一样的就是没有在[]之间给定长度。上面就创建一个长度为0的x变量。
如果你要创建一个slice,你可以调用内置函数”make”:
x := make([]int, 5)
这样就创建一个slice,它会跟一个长度为5的数组关联起来。Slices总是跟数组相关联的,它必须小于数组的长度。
text/template
用法:
import “text/template”
很多服务器端的语言都提供一种机制来定义一些静态的页面,然后往里面插入动态生成的组件,比如插入一个列表。典型的例子JSP/PHP里面的脚本。GO在template这个package也提供了类似的相关的脚本语言。
这个包以文本作为输入,基于对对象值的解释,把原来的输入文本转换成其它的不一样的文本。
为了生成HTML格式的文本,我们可以很快去学习关于”html/template”的package,它提供了跟text/template相同的接口,但可以自动使输出的HTML免于某些攻击。
template会作用在一个Go object上。Go object里面的field可以被插入到template,你也可以挖掘这个object来得到它所有的field,等等。可以用”.“来代表当前这个object, 因此如果想把这个object的值作为String插入到template,你可以这样用”{{.}}“。package默认会使用”fmt”这个package来生成字符串作为插入到template的值。
如果只是想把当前object的一个field的值插入到template,你可以通过在这个field的名字前面加一个前缀 “.”。 假设我们定义这样一个类型的对象:
type Student struct { Name string }
然后你就可以通过下面的方式插入Name的值:
The name is @H_404_137@{{.Name}}.
所以template是一种将通用文本(不会变化的)跟特定文本作merge的方法,例如tempate可以保留里面通用的文本,然后根据需要替换一些特定的文本。
这种定义的语法就是把template的声明用“define”和“end” action包起来。
define这个action后面跟着一个字符串来指定template的名字
,下面就是一个例子:
`@H_404_137@{{define "T1"}}ONE@H_404_137@{{end}} @H_404_137@{{define "T2"}}TWO@H_404_137@{{end}} @H_404_137@{{define "T3"}}@H_404_137@{{template "T1"}} @H_404_137@{{template "T2"}}@H_404_137@{{end}} @H_404_137@{{template "T3"}}`
这里定义了两个template,T1和T2,第三个T3执行时会调用了其它两个template。最后调用T3。如果运行T3这个template,它会输出下面的文本:
ONE TWO
在Go语言中,我们用template这个package以及它的”Parse”, “ParseFile”, “Execute”之类的方法来从字符串或者文件中加载一个template,然后做上面提到的文本的转换和merge。 需要merge的内容通常在一个自定义的类型里面,这个类型包含一些可以被外面的包访问的field,例如template用到的一些定义在struct这个类型里面的filed就需要以大写字母开头。
让我们看一个简单的例子。
创建下面一个目录并且切换到这个目录:
$ mkdir $GOPATH/src/github.com/SatishTalim/texttmpl
$ cd $GOPATH/src/github.com/SatishTalim/texttmpl
在这个目录下面写个名字叫”stud_struct.go”的程序,如下:
package main
import (
"log"
"os"
"text/template"
)
type Student struct {
//exported field since it begins
//with a capital letter
Name string
}
func main() {
//define an instance
s := Student{"Satish"}
//create a new template with some name
tmpl := template.New("test")
//parse some content and generate a template
tmpl,err := tmpl.Parse("Hello {{.Name}}!")
if err != nil {
log.Fatal("Parse: ",err)
return
}
//merge template 'tmpl' with content of 's'
err1 := tmpl.Execute(os.Stdout,s)
if err1 != nil {
log.Fatal("Execute: ",err1)
return
}
}
你可以敲如下的命令来执行这个程序:
$ go run stud_struct.go
输出结果:
Hello Satish!
注意:
- “New”关键字创建了一个指定名字的新template。
- “Parse”将一个字符串解释成template
- 要将某个field的内容放到template里面,就将这个field用大括号包起来,并且在这个field的前面加一个点号。例如,如果Name 是某个struct的一个field,并且在merge的时候需要将它的值替换进去,则需要把“{{.Name}}”放到template。但要记住这个field必需是存在的并且它是对外可见的(在类型定义中以大写字母开头),否则会报错。所有”{{.Name}}”外面的文本会被原封不动地拷贝到输出的文本中。
- 我们这里用预先定义好的变量”os.Stdout”来将merge之后的文本输出到标准输出设备中 - “os.Stdout” 实现了”io.Writer”
- “Execute”方法将一个已经加载的模板应用到指定的数据object上,并且将输出到”os.Stdout”
让我们看另外一个例子。
创建下面一个目录并且切换到这个目录:
$ mkdir $GOPATH/src/github.com/SatishTalim/person
$ cd $GOPATH/src/github.com/SatishTalim/person
在这个目录下写一个”person.go”的程序:
package main
import (
"log"
"os"
"text/template"
)
type Person struct {
Name string
Emails []string
}
const tmpl = `The name is {{.Name}}.
{{range .Emails}}
His email id is {{.}}
{{end}}
`
func main() {
person := Person{
Name: "Satish",Emails: []string{"satish@rubylearning.org","satishtalim@gmail.com"},}
t := template.New("Person template")
t,err := t.Parse(tmpl)
if err != nil {
log.Fatal("Parse: ",err)
return
}
err = t.Execute(os.Stdout,person)
if err != nil {
log.Fatal("Execute: ",err)
return
}
}
敲入如下命令来运行这个程序:
$ go run person.go
输出结果:
The name is Satish.
His email id is satish@rubylearning.org
His email id is satishtalim@gmail.com
Go的template package在文本转换时非常有用,特别是当这个转换需要插入对象的值时。它虽没有正则表达式那么强大,但却更快并且很多情况下比正则表达式好用。
html/template
Usage:
import “html/template”
“html/template”这个包实现了data-driven template,用来生成HTML输出,比直接的代码注入更安全。它提供了跟”text/template”一样的接口,当输出的格式是HTML是应该用”html/template”这个package,而不是”text/template”。