go标准库的学习-text/template
参考:https://studygolang.com/pkgdoc
导入方式:
import "text/template"
template包实现了数据驱动的用于生成文本输出的模板。其实简单来说就是将一组文本嵌入另一组文本模版中,返回一个你期望的文本
如果要生成HTML格式的输出,参见html/template包,该包提供了和本包相同的接口,但会自动将输出转化为安全的HTML格式输出,可以抵抗一些网络攻击。
用作模板的输入文本必须是utf-8编码的文本。"Action",即数据运算和控制单位由"{{"和"}}"界定(即{{Action}});在Action之外的所有文本都不做修改的拷贝到输出中。Action内部不能有换行,但注释可以有换行。
{{Action}}中的运算可以通过()进行分组,如:
//执行结果可以访问其字段或者键对应的值:
print (.F1 arg1) (.F2 arg2)
(.StructValuedMethod "arg").Field
经解析生成模板后,一个模板可以安全的并发执行。
有两个常用的传入参数的类型:
- 一个是struct,在模板内可以读取该struct域的内容来进行渲染。下面的例子中使用更多的是这种方法,即自定义一个struct,然后将其值作为Execute()函数的第二个参数,然后.就表示该对象,然后通过它来调用相应的字符串值,甚至是函数
- 一个是map[string]interface{},在模板内可以使用key来进行渲染
type Template
type Template struct {
*parse.Tree
// 内含隐藏或非导出字段
}
代表一个解析好的模板,*parse.Tree字段仅仅是暴露给html/template包使用的,因此其他人应该视字段未导出。
func New
func New(name string) *Template
创建一个名为name的模板。
func (*Template) Parse
func (t *Template) Parse(text string) (*Template, error)
Parse方法将字符串text解析为模板。嵌套定义的模板会关联到最顶层的t。Parse可以多次调用,但只有第一次调用可以包含空格、注释和模板定义之外的文本。如果后面的调用在解析后仍剩余文本会引发错误、返回nil且丢弃剩余文本;如果解析得到的模板已有相关联的同名模板,会覆盖掉原模板。
func (*Template) Execute
func (t *Template) Execute(wr io.Writer, data interface{}) (err error)
Execute方法将解析好的模板应用到data上,并将输出写入wr。如果执行时出现错误,会停止执行,但有可能已经写入wr部分数据。模板可以安全的并发执行。
1.调用的是变量时
1)举一个最简单的例子,传入的是string字符串:
package main
import(
"os"
"text/template"
) func main() {
str := "world"
tmpl, err := template.New("test").Parse("hello, {{.}}\n") //建立一个名字为test的模版"hello, {{.}}"
if err != nil{
panic(err)
}
err = tmpl.Execute(os.Stdout, str) //将str的值合成到tmpl模版的{{.}}中,并将合成得到的文本输入到os.Stdout,返回hello, world
if err != nil{
panic(err)
}
}
2)另一个例子,传入的是struct对象的值:
package main
import(
"os"
"text/template"
) type Inventory struct {
Material string
Count uint
} func main() {
sweaters := Inventory{"wool", }
tmpl, err := template.New("test").Parse("{{.Count}} of {{.Material}}\n")//{{.Count}}获取的是struct对象中的Count字段的值
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)//返回 17 of wool
if err != nil { panic(err) }
}
如果上面的例子中Count的值也是一个struct对象,可以使用{{.Count.Field1}}来访问其字段
3)自定义的变量,可以先看下面的方法的例子
举例:
package main
import(
"os"
"text/template"
)
type MyMethod struct{
Say string
Name string
} func (my *MyMethod)SayHello() string{//没参数
return "world"
} func (my *MyMethod)SayYouName(name string) string { //有参数
return "my name is : " + name
} func main() {
mine := &MyMethod{ Say : "hello", Name : "student"}
//先对变量$str1,$str2,$str3赋值,一个是直接将字符串值赋值,另两个是调用函数,将返回值赋值,然后再将变量值输出
tmpl, err := template.New("test").Parse("{{$str1 := .Say}}{{$str2 := .SayHello}}{{$str3 := .SayYouName .Name}}{{$str1}} {{$str2}}\n{{$str3}}\n")
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, mine)
if err != nil {
panic(err)
}
}
返回:
bogon:~ user$ go run testGo.go
hello world
my name is : student
2.函数:
执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用Funcs方法添加函数到模板里。
方法必须有一到两个返回值,如果是两个,那么第二个一定是error接口类型
1)模版内定义函数
举例:
package main
import(
"os"
"text/template"
)
type MyMethod struct{
Name string
} func (my *MyMethod)SayHello() string{//没参数
return "hello world"
} func (my *MyMethod)SayYouName(name string) string { //有参数
return "my name is : " + name
} func main() {
mine := &MyMethod{ Name : "boss"}
tmpl, err := template.New("test").Parse("{{.SayHello}}\n{{.SayYouName .Name}}\n")
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, mine)
if err != nil {
panic(err)
}
}
返回:
bogon:~ user$ go run testGo.go
hello world
my name is : boss
2)使用Funcs方法添加函数到模板
func (*Template) Funcs
func (t *Template) Funcs(funcMap FuncMap) *Template
Funcs方法向模板t的函数字典里加入参数funcMap内的键值对。如果funcMap某个键值对的值不是函数类型或者返回值不符合要求会panic。但是,可以对t函数列表的成员进行重写。方法返回t以便进行链式调用。
type FuncMap
type FuncMap map[string]interface{}
FuncMap类型定义了函数名字符串到函数的映射,每个函数都必须有1到2个返回值,如果有2个则后一个必须是error接口类型;如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用者该错误。
举例:
package main
import(
"os"
"text/template"
) func SayHello() string{//没参数
return "hello world"
} func SayYouName(name string) string { //有参数
return "my name is : " + name
} func main() {
funcMap := template.FuncMap{
//在FuncMap中声明相应要使用的函数,然后就能够在template字符串中使用该函数
"SayHello" : SayHello,
"SayYouName" : SayYouName,
}
name := "boss"
tmpl, err := template.New("test").Funcs(funcMap).Parse("{{SayHello}}\n{{SayYouName .}}\n")
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, name)
if err != nil {
panic(err)
}
}
返回:
bogon:~ user$ go run testGo.go
hello world
my name is : boss
根据pipeline的定义,上面的{{SayYouName .}}等价于{{.|SayYouName}}
预定义的全局函数如下:
就是在{{}}中可以直接使用的函数
and
函数返回它的第一个empty参数或者最后一个参数;
就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or
返回第一个非empty参数或者最后一个参数;
亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not
返回它的单个参数的布尔值的否定
len
返回它的参数的整数类型长度
index
执行结果为第一个参数以剩下的参数为索引/键指向的值;
如"index x 1 2 3"返回x[][][]的值;每个被索引的主体必须是数组、切片或者字典。
即fmt.Sprint
printf
即fmt.Sprintf
println
即fmt.Sprintln
html
返回其参数文本表示的HTML逸码等价表示。
urlquery
返回其参数文本表示的可嵌入URL查询的逸码等价表示。
js
返回其参数文本表示的JavaScript逸码等价表示。
call
执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
如"call .X.Y 1 2"等价于go语言里的dot.X.Y(, );
其中Y是函数类型的字段或者字典的值,或者其他类似情况;
call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;
如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;
布尔函数会将任何类型的零值视为假,其余视为真。
举例:
package main
import(
"os"
"text/template"
) func main() {
name := "boss"
tmpl, err := template.New("test").Parse(`{{printf "%q\n" .}}`)
if err != nil {
panic(err)
}
err = tmpl.Execute(os.Stdout, name)
if err != nil {
panic(err)
}
}
返回:
bogon:~ user$ go run testGo.go
"boss"
下面是定义为函数的二元比较运算的集合:
eq 如果arg1 == arg2则返回真
ne 如果arg1 != arg2则返回真
lt 如果arg1 < arg2则返回真
le 如果arg1 <= arg2则返回真
gt 如果arg1 > arg2则返回真
ge 如果arg1 >= arg2则返回真
为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:
arg1==arg2 || arg1==arg3 || arg1==arg4 ...
(和go的||不一样,不做惰性运算,所有参数都会执行)
比较函数只适用于基本类型(或重定义的基本类型,如"type Celsius float32")。它们实现了go语言规则的值的比较,但具体的类型和大小会忽略掉,因此任意类型有符号整数值都可以互相比较;任意类型无符号整数值都可以互相比较;等等。但是,整数和浮点数不能互相比较。
3.pipelines
这上面举的所有例子中的{{ }}内的操作我们将其称作pipelines
pipeline通常是将一个command序列分割开,再使用管道符'|'连接起来(但不使用管道符的command序列也可以视为一个管道),上面的例子都是最简单的pipelines的类型,因为一个{{}}中只有一个command。而上面自定义变量中的语法为:
$variable := pipeline
更复杂的有:
range $index, $element := pipeline
这时,$index和$element分别设置为数组/切片的索引或者字典的键,以及对应的成员元素。注意这和go range从句只有一个参数时设置为索引/键不同!
一个变量的作用域只到声明它的控制结构("if"、"with"、"range")的"end"为止,如果不是在控制结构里声明会直到模板结束为止。子模板的调用不会从调用它的位置(作用域)继承变量。
如果没有定义变量的名字,而是只使用$,那么在模板开始执行时,$会设置为传递给Execute方法的参数,就是说,dot的初始值。
在一个链式的pipeline里,每个command的结果都作为下一个command的最后一个参数。pipeline最后一个command的输出作为整个管道执行的结果。
command的输出可以是1到2个值,如果是2个后一个必须是error接口类型。如果error类型返回值非nil,模板执行会中止并将该错误返回给执行模板的调用者。
Actions
下面是一个action(动作)的列表。"Arguments"和"pipelines"代表数据的执行结果,细节定义在后面。
{{/* a comment */}}
注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止,就像这里表示的一样。
{{pipeline}}
pipeline的值的默认文本表示会被拷贝到输出里。
{{if pipeline}} T1 {{end}}
如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。
Empty值包括false、、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
{{if pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值为empty,输出T0执行结果,否则输出T1执行结果。不改变dot的值。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
用于简化if-else链条,else action可以直接包含另一个if;等价于:
{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
{{range pipeline}} T1 {{end}}
pipeline的值必须是数组、切片、字典或者通道。
如果pipeline的值其长度为0,不会有任何输出;
否则dot依次设为数组、切片、字典或者通道的每一个成员元素并执行T1;
如果pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。
{{range pipeline}} T1 {{else}} T0 {{end}}
pipeline的值必须是数组、切片、字典或者通道。
如果pipeline的值其长度为0,不改变dot的值并执行T0;否则会修改dot并执行T1。
{{template "name"}}
执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""
{{template "name" pipeline}}
执行名为name的模板,提供给模板的参数为pipeline的值。
{{with pipeline}} T1 {{end}}
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。
{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。
4.条件判断-if
{{if pipeline}} T1 {{end}}
如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。
Empty值包括false、、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
{{if pipeline}} T1 {{else}} T0 {{end}}
如果pipeline的值为empty,输出T0执行结果,否则输出T1执行结果。不改变dot的值。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
用于简化if-else链条,else action可以直接包含另一个if;等价于:
{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
将其与全局函数结合使用为:
{{if not .condition}}
{{end}}
{{if and .condition1 .condition2}} //即如果condition1成立则返回condition2,否则返回condition1
{{end}}
{{if or .condition1 .condition2}} //即如果condition1成立则返回condition1,否则返回condition2
{{end}}
{{if eq .var1 .var2}}
{{end}}
...
还有:
{{with pipeline}} T1 {{end}}
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。
{{with pipeline}} T1 {{else}} T0 {{end}}
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。
func Must
func Must(t *Template, err error) *Template
Must函数用于包装返回(*Template, error)的函数/方法调用,它会在err非nil时panic,一般用于变量初始化:
var t = template.Must(template.New("name").Parse("text"))
这样就不用像上面的例子一样还要使用if err != nil来判断是否出错
举例:
package main
import(
"os"
"text/template"
"log"
) func main() {
//创建一个模版
const letter = `
Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.
{{- else}}
It is a shame you couldn't make it to the wedding.
{{- end}}
{{with .Gift -}}
Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
`
type Recipient struct {
Name, Gift string
Attended bool
}
var recipients = []Recipient{
{"Aunt Mildred", "bone china tea set", true},
{"Uncle John", "moleskin pants", false},
} // Create a new template and parse the letter into it.
t := template.Must(template.New("letter").Parse(letter)) // Execute the template for each recipient.
for _, r := range recipients {
err := t.Execute(os.Stdout, r)
if err != nil {
log.Println("executing template:", err)
}
}
}
返回:
bogon:~ user$ go run testGo.go Dear Aunt Mildred, It was a pleasure to see you at the wedding.
Thank you for the lovely bone china tea set. Best wishes,
Josie Dear Uncle John, It is a shame you couldn't make it to the wedding.
Thank you for the lovely moleskin pants. Best wishes,
Josie
注意:
- 在{{- else}}、{{- end}}和{{with .Gift -}}中的-表示消除{{else}}等会导致的空行
- {{with .Gift}}表示如果Gift不为空的话,则打印下面的句子
5.遍历-range
{{range pipeline}} T1 {{end}}
pipeline的值必须是数组、切片、字典或者通道。
如果pipeline的值其长度为0,不会有任何输出;
否则dot依次设为数组、切片、字典或者通道的每一个成员元素并执行T1;
如果pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。
{{range pipeline}} T1 {{else}} T0 {{end}}
pipeline的值必须是数组、切片、字典或者通道。
如果pipeline的值其长度为0,即没有可遍历的值时,不改变dot的值并执行T0;否则会修改dot并执行T1。
常见用法有:
{{range $i, $v := .Var}} //显示得到遍历的index和value
{{$i}} => {{$v}}
{{end}} {{range .Var}} //没有显示去获取遍历得到的index和value,这时候要获得value值,使用{{.}}表示
{{.}}
{{end}} {{range .slice}} //如果想要在range...end中访问非遍历得到的value,即外部的其他值,则在前面添加$来表示
{{$.OutsideContent}}
{{end}}
举例:
package main
import(
"os"
"text/template"
"log"
) func main() {
//创建一个模版
rangeTemplate := `
{{if .Kind}}
{{range $i, $v := .MapContent}}
{{$i}} => {{$v}} , {{$.OutsideContent}}
{{end}}
{{else}}
{{range .MapContent}}
{{.}} , {{$.OutsideContent}}
{{end}}
{{end}}` str1 := []string{"this is the first range", "use its index and value"}
str2 := []string{"this is the second range", "do not use its index and value"} type Content struct {
MapContent []string
OutsideContent string
Kind bool
}
var contents = []Content{
{str1, "this is the first outside content", true},
{str2, "this is the second outside content", false},
} // Create a new template and parse the letter into it.
t := template.Must(template.New("range").Parse(rangeTemplate)) // Execute the template for each recipient.
for _, c := range contents {
err := t.Execute(os.Stdout, c)
if err != nil {
log.Println("executing template:", err)
}
}
}
返回:
bogon:~ user$ go run testGo.go => this is the first range , this is the first outside content => use its index and value , this is the first outside content this is the second range , this is the second outside content do not use its index and value , this is the second outside content
模版回车
上面的空行与模版中的回车有关,如果想要没有输出的空行,上面的模版应该写成:
rangeTemplate := `{{if .Kind}}{{range $i, $v := .MapContent}}
{{$i}} => {{$v}} , {{$.OutsideContent}}
{{end}}
{{else}}{{range .MapContent}}
{{.}} , {{$.OutsideContent}}
{{end}}
{{end}}`
6.模版嵌套
{{template "name"}}
执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""。当然首先要使用{{define "name"}}{{end}}定义好该模版
{{template "name" pipeline}}
执行名为name的模板,提供给模板的参数为pipeline的值。将管道的值赋给子模板中的"."(即"{{.}}"),即{{template "name" .}}
1)使用{{define "name"}}...{{end}}定义模版
举例1:
package main
import(
"os"
"text/template"
"log"
) func main() {
//创建一个模版
templateContent := `{{define "T1"}}ONE{{end}}{{define "T2"}}TWO{{end}}{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}{{template "T3"}}` // Create a new template and parse the letter into it.
t := template.Must(template.New("template").Parse(templateContent)) // Execute the template for each recipient.
err := t.Execute(os.Stdout, nil)
if err != nil {
log.Println("executing template:", err)
}
}
返回:
bogon:~ user$ go run testGo.go
ONE TWObogon:~ user$
2)使用template.New("name")定义模版
举例2:
等价于上面的例子,只是写法不同
package main
import(
"os"
"text/template"
"log"
) func main() {
//创建一个模版
template1 := "ONE"
template2 := "TWO"
template3 := `{{template "T1"}} {{template "T2"}}` // Create a new template and parse the letter into it.
t := template.Must(template.New("T1").Parse(template1))
t = template.Must(t.New("T2").Parse(template2))
t = template.Must(t.New("T3").Parse(template3)) // Execute the template for each recipient.
err := t.Execute(os.Stdout, nil)
if err != nil {
log.Println("executing template:", err)
}
}
返回:
bogon:~ user$ go run testGo.go
ONE TWObogon:~ user$
7.多模版
其实在上面的模版嵌套中我们就使用了多模版的概念
func (*Template) New
func (t *Template) New(name string) *Template
New方法创建一个和t关联的名字为name的模板并返回它。这种可以传递的关联允许一个模板使用template action调用另一个模板。
func (*Template) Lookup
func (t *Template) Lookup(name string) *Template
Lookup方法返回与t关联的名为name的模板,如果没有这个模板返回nil。
func (*Template) Templates
func (t *Template) Templates() []*Template
Templates方法返回与t相关联的模板的切片,包括t自己。
当一个Template中有多个模版时,你需要指定解析的模版,因此在这里使用的是ExecuteTemplate,而不是Execute
func (*Template) Name
func (t *Template) Name() string
返回模板t的名字。
func (*Template) ExecuteTemplate
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
ExecuteTemplate方法类似Execute,但是使用名为name的t关联的模板产生输出。
举例:
package main
import(
"os"
"text/template"
"fmt"
) type Inventory struct {
Material string
Count uint
} func main() {
sweaters := Inventory{"wool", }
template1 := "{{.Count}} of {{.Material}}\n"
template2 := "{{.Material}} of {{.Count}}\n" tmpl := template.Must(template.New("T1").Parse(template1))
fmt.Println(tmpl.Name()) //T1
tmpl = template.Must(tmpl.New("T2").Parse(template2))
fmt.Println(tmpl.Name()) //T2 err := tmpl.ExecuteTemplate(os.Stdout, "T1", sweaters)//返回 17 of wool
if err != nil { panic(err) }
err = tmpl.ExecuteTemplate(os.Stdout, "T2", sweaters)//返回 wool of 17
if err != nil { panic(err) } tmpl = tmpl.Lookup("T1")
fmt.Println(tmpl.Name()) //T1 mapTemplate := tmpl.Templates()
for _, v := range mapTemplate{ //先得到T2,再得到T1
fmt.Println(v.Name())
} }
返回:
bogon:~ user$ go run testGo.go
T1
T2
of wool
wool of
T1
T2
T1
8.文件模版
其实就是你可以将你的模版内容写到文件当中,然后再从文件中调用
func ParseFiles
func (t *Template) ParseFiles(filenames ...string) (*Template, error)
ParseFiles函数创建一个模板并解析filenames指定的文件里的模板定义。返回的模板的名字是第一个文件的文件名(不含扩展名),内容为解析后的第一个文件的内容。至少要提供一个文件。如果发生错误,会停止解析并返回nil。
func ParseGlob
func (t *Template) ParseGlob(pattern string) (*Template, error)
ParseGlob创建一个模板并解析匹配pattern的文件(参见glob规则)里的模板定义。返回的模板的名字是第一个匹配的文件的文件名(不含扩展名),内容为解析后的第一个文件的内容。至少要存在一个匹配的文件。如果发生错误,会停止解析并返回nil。ParseGlob等价于使用匹配pattern的文件的列表为参数调用ParseFiles。
1)ParseFiles—一个模版文件
ParseFiles接受一个字符串,字符串的内容是一个模板文件的路径(绝对路径or相对路径)
将模版写到文件templateContent.txt中:
{{.Count}} of {{.Material}}
例子:
package main
import(
"os"
"text/template"
) type Inventory struct {
Material string
Count uint
} func main() {
sweaters := Inventory{"wool", } tmpl, err := template.ParseFiles("templateContent.txt")//这里不需要使用new(),因为会默认使用文件名来命名
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)//返回 17 of wool
if err != nil { panic(err) }
}
返回:
bogon:~ user$ cat templateContent.txt
{{.Count}} of {{.Material}}bogon:~ user$
bogon:~ user$ go run testGo.go
of woolbogon:~ user$
2)ParseGlob—多个模版文件
ParseGlob是用正则的方式匹配多个文件,如当你要选取某文件夹下的所有txt模版文件时,就可以调用ParseGlob("*.txt")
比如修改上面多模版的例子:
templateContent.txt(回车一行来实现换行)
{{.Count}} of {{.Material}}
anotherTemplate.txt(回车一行来实现换行)
{{.Material}} of {{.Count}}
举例:
package main
import(
"os"
"text/template"
"fmt"
) type Inventory struct {
Material string
Count uint
} func main() {
sweaters := Inventory{"wool", } tmpl := template.Must(template.ParseGlob("*.txt")) mapTemplate := tmpl.Templates()
for _, v := range mapTemplate{ //先得到anotherTemplate.txt,再得到templateContent.txt
fmt.Println(v.Name())
} err := tmpl.ExecuteTemplate(os.Stdout, "templateContent.txt", sweaters)//返回 17 of wool
if err != nil { panic(err) }
err = tmpl.ExecuteTemplate(os.Stdout, "anotherTemplate.txt", sweaters)//返回 wool of 17
if err != nil { panic(err) } }
返回:
bogon:~ user$ cat templateContent.txt
{{.Count}} of {{.Material}}
bogon:~ user$ cat anotherTemplate.txt
{{.Material}} of {{.Count}}
bogon:~ user$ go run testGo.go
anotherTemplate.txt
templateContent.txt
of wool
wool of
9.其他
func (*Template) Clone
func (t *Template) Clone() (*Template, error)
Clone方法返回模板的一个副本,包括所有相关联的模板。模板的底层表示树并未拷贝,而是拷贝了命名空间,因此拷贝调用Parse方法不会修改原模板的命名空间。Clone方法用于准备模板的公用部分,向拷贝中加入其他关联模板后再进行使用。
例子:
// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
// T0.tmpl is a plain template file that just invokes T1.
{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)
// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")
// Here starts the example proper.
// Load the drivers.
drivers := template.Must(template.ParseGlob(pattern))
// We must define an implementation of the T2 template. First we clone
// the drivers, then add a definition of T2 to the template name space.
// 1. Clone the helper set to create a new name space from which to run them.
first, err := drivers.Clone()
if err != nil {
log.Fatal("cloning helpers: ", err)
}
// 2. Define T2, version A, and parse it.
_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
if err != nil {
log.Fatal("parsing T2: ", err)
}
// Now repeat the whole thing, using a different version of T2.
// 1. Clone the drivers.
second, err := drivers.Clone()
if err != nil {
log.Fatal("cloning drivers: ", err)
}
// 2. Define T2, version B, and parse it.
_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
if err != nil {
log.Fatal("parsing T2: ", err)
}
// Execute the templates in the reverse order to verify the
// first is unaffected by the second.
err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
if err != nil {
log.Fatalf("second execution: %s", err)
}
err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
if err != nil {
log.Fatalf("first: execution: %s", err)
}
这样对first或second的操作都不会影响drivers,first和second之间也互不影响
返回:
T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
func (*Template) Delims
func (t *Template) Delims(left, right string) *Template
Delims方法用于设置action的分界字符串,应用于之后的Parse、ParseFiles、ParseGlob方法。嵌套模板定义会继承这种分界符设置。空字符串分界符表示相应的默认分界符:{{或}}。返回值就是t,以便进行链式调用。
默认的分界符即left = {{ , right = }}
如果把分界符改成<>,举例:
package main
import(
"os"
"text/template"
) func main() {
str := "world"
tmpl, err := template.New("test").Delims("<",">").Parse("hello, <.>\n") //建立一个名字为test的模版"hello, <.>}"
if err != nil{
panic(err)
}
err = tmpl.Execute(os.Stdout, str) //将str的值合成到tmpl模版的{{.}}中,并将合成得到的文本输入到os.Stdout,返回hello, world
if err != nil{
panic(err)
}
}
返回:
bogon:~ user$ go run testGo.go
hello, world
func HTMLEscape
func HTMLEscape(w io.Writer, b []byte)
函数向w中写入b的HTML转义等价表示。
func HTMLEscapeString
func HTMLEscapeString(s string) string
返回s的HTML转义等价表示字符串。
func HTMLEscaper
func HTMLEscaper(args ...interface{}) string
函数返回其所有参数文本表示的HTML转义等价表示字符串。
举例:
package main
import(
"fmt"
"net/http"
"log"
"text/template"
) func index(w http.ResponseWriter, r *http.Request){
r.ParseForm() //解析URL传递的参数,对于POST则解析响应包的主体(request body),如果不调用它则无法获取表单的数据
fmt.Println(r.Form)
fmt.Println(r.PostForm)
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"]) //如果使用的是方法FormValue()方法(它只会返回同名参数slice中的第一个,不存在则返回空字符串),则可以不用调用上面的ParseForm()方法
for k, v := range r.Form{
fmt.Println("key :", k)
fmt.Println("value :", v)
}
fmt.Fprintf(w, "hello world") //将html写到w中,w中的内容将会输出到客户端中
} func login(w http.ResponseWriter, r *http.Request){
fmt.Println("method", r.Method) //获得请求的方法
r.ParseForm()
if r.Method == "GET"{ //
html := `<html>
<head>
<title></title>
</head>
<body>
<form action="http://localhost:9090/login" method="post">
username: <input type="text" name="username">
password: <input type="text" name="password">
<input type="submit" value="login">
</form>
</body>
</html>`
t := template.Must(template.New("test").Parse(html))
t.Execute(w, nil)
}else{
fmt.Println("username : ", template.HTMLEscapeString(r.Form.Get("username")))//在终端即客户端输出
fmt.Println("password : ", template.HTMLEscapeString(r.Form.Get("password")))//把r.Form.Get("password")转义之后返回字符串
template.HTMLEscape(w, []byte(r.Form.Get("username"))) //在客户端输出,把r.Form.Get("username")转义后写到w
}
} func main() {
http.HandleFunc("/", index) //设置访问的路由
http.HandleFunc("/login", login) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil{
log.Fatal("ListenAndServe : ", err)
}
}
访问http://localhost:9090/
访问http://localhost:9090/login
如果仅传入字符串:
服务端返回:
method POST
username : hello
password : allen
map[]
map[]
path /favicon.ico
scheme
[]
客户端:
当时如果username输入的是<script>alert()</script>
客户端返回:
可见html/template包默认帮你过滤了html标签
func JSEscape
func JSEscape(w io.Writer, b []byte)
函数向w中写入b的JavaScript转义等价表示。
func JSEscapeString
func JSEscapeString(s string) string
返回s的JavaScript转义等价表示字符串。
func JSEscaper
func JSEscaper(args ...interface{}) string
函数返回其所有参数文本表示的JavaScript转义等价表示字符串。
func URLQueryEscaper
func URLQueryEscaper(args ...interface{}) string
函数返回其所有参数文本表示的可以嵌入URL查询的转义等价表示字符串。
go标准库的学习-text/template的更多相关文章
- go标准库的学习-net/http
参考:https://studygolang.com/pkgdoc 概念解释: request:用户请求的信息,用来解析用户的请求信息,包括post.get.cookie.url等信息 respons ...
- go标准库的学习-database/sql
参考:https://studygolang.com/pkgdoc 导入方式: import "database/sql" sql包提供了保证SQL或类SQL数据库的泛用接口. 使 ...
- go标准库的学习-crypto/md5
参考:https://studygolang.com/pkgdoc 导入方式: import "crypto/md5" md5包实现了MD5哈希算法,参见RFC 1321. Con ...
- go标准库的学习-crypto/sha1
参考:https://studygolang.com/pkgdoc 导入方式: import "crypto/sha1" sha1包实现了SHA1哈希算法,参见RFC 3174. ...
- go标准库的学习-crypto/sha256
参考:https://studygolang.com/pkgdoc 导入方式: import "crypto/sha256" sha256包实现了SHA224和SHA256哈希算法 ...
- python linecache标准库基础学习
#python标准库基础之:linecacge:高效读取文本文件#说明与作用"""可以从文件或者导入python模块获取文件,维护一个结果缓存,从而可以更高效地从相同文件 ...
- python 标准库基础学习之开发工具部分1学习
#2个标准库模块放一起学习,这样减少占用地方和空间#标准库之compileall字节编译源文件import compileall,re,sys#作用是查找到python文件,并把它们编译成字节码表示, ...
- python calendar标准库基础学习
# -*- coding: utf-8 -*-# 作者:新手__author__ = 'Administrator'#标准库:日期时间基础学习:calendar:处理日期#例1import calen ...
- 《C标准库》学习笔记整理
简介 <C标准库>书中对 C 标准库中的 15 个头文件的内容进行了详细的介绍,包括各头文件设计的背景知识.头文件中的内容.头文件中定义的函数和变量的使用.实现.测试等. 我学习此书的目的 ...
随机推荐
- swiper下滑分页,减少swiper-slide项的时候会出现空白
修改子项后,先重置当前的页,调用 swiper.slideTo(0); 滚动到初始位置 再调用 swiper.update(); 更新一系列设置就可以了.
- 关于height,line-height导致的样式混乱的问题
设置固定border,padding,line-height,但是发现在ie下input标签竟然line-height不起作用,导致实际的高度无法控制 解决方法:添加固定高度height.
- angularjs学习第七天笔记(系统指令学习)
您好,接着在昨天对简单指令学习了解以后,今天开始学习了解angularjs中的系统指令 系统指令大部分都是以ng开始,这也是为什么在自定义指令命名时不要以ng开始的原因所在 系统指令在学习了分成两个部 ...
- Python3.7 数字之间下划线
只是为了提高可读性,数值没变. >>> yes_votes = 42_572_6540 ; >>> yes_votes = 42_572_654099 ; > ...
- Numbers、Strings、Lists 笔记<一>
下一篇:流程控制<二> 阅读链接:官方Python3.7教程 废话:最近开始阅读python3.7文档,希望把容易混淆的知识记下来. 除法总是返回一个浮点数 >>> 8/ ...
- jquery中innerwidth,outerwidth,outerwidth和width的区别
在jQuery中,width()方法用于获得元素宽度: innerWidth()方法用于获得包括内边界(padding)的元素宽度, outerWidth()方法用于获得包括内边界(padding)和 ...
- design mode(php)
前段时间看了下设计模式 参考,以及head first设计模式,简要如下 ## OO原则 * 封装变化* 多用组合,少用继承* 针对接口编程,不针对实现编程* 为交互对象之间的松耦合设计而努力* 开闭 ...
- JS 中的 __proto__ 、prototype、constructor
首先 先解释这三个属性: (1) prototype : 它是函数独有的,从一个函数指向一个对象(函数的原型),含义是函数的原型对象,也就是这个函数所创建的实例的原型对象.(普通函数的该属性没有作用 ...
- Python 简单的远程执行命令
client端执行命令,server端返回命令结果 # server 端 import socket, subprocess sk = socket.socket() address=('127.0. ...
- 通过了解JS的clientX、pageX、screenX等方法来获取鼠标位置相对屏幕,相对浏览器窗口,相对文档的坐标详解
在一些DOM操作中我们经常会跟元素的位置打交道,鼠标交互式一个经常用到的方面,令人失望的是不同的浏览器下会有不同的结果甚至是有的浏览器下没结果,这篇文章就上鼠标点击位置坐标获取做一些简单的总结,没特殊 ...