当前位置:网站首页>golang模板(text/template)
golang模板(text/template)
2022-06-30 12:50:00 【IT工作者】
text/template是Go语言标准库,实现数据驱动模板以生成文本输出,可以理解为一组文字按照特定格式动态嵌入另一组文字中。
还有个处理html文字的模板(html/template),感兴趣的可以了解下。
简单字符
示例
package main
import (
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
name := "world"
tmpl, err := template.New("test").Parse("hello, {{.}}") //建立一个模板,内容是"hello, {{.}}"
CheckErr(err)
err = tmpl.Execute(os.Stdout, name) //将string与模板合成,变量name的内容会替换掉{{.}}
//合成结果放到os.Stdout里 输出
CheckErr(err)
}输出: hello, world
模板的输入文本是任何格式的UTF-8编码文本。 {{ 和 }} 包裹的内容统称为 action,分为两种类型:
数据求值(data evaluations)
控制结构(control structures)
action 求值的结果会直接复制到模板中,控制结构和我们写 Go 程序差不多,也是条件语句、循环语句、变量、函数调用等等…
将模板成功解析(Parse)后,可以安全地在并发环境中使用,如果输出到同一个 io.Writer 数据可能会重叠(因为不能保证并发执行的先后顺序)。
这里{{和}}中间的句号(.)代表传入的数据,数据不同渲染不同,可以代表 go 语言中的任何类型,如结构体、哈希等。
struct
示例
package main
import (
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
CheckErr(err)
err = tmpl.Execute(os.Stdout, sweaters)
CheckErr(err)
}输出:17 items are made of wool
模板文件
新建一个模板文件 demo.go.tpl
内容
{{.Count}} items are made of {{.Material}}
上面示例可改为
package main
import (
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.ParseFiles("demo.go.tpl")
CheckErr(err)
err = tmpl.Execute(os.Stdout, sweaters)
CheckErr(err)
}输出:17 items are made of wool
多模板
示例
package main
import (
"fmt"
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("T2").Parse("{{.Count}} items are made of")
tmpl, err = tmpl.New("test").Parse("{{.Count}} items are made of {{.Material}}")
CheckErr(err)
err = tmpl.ExecuteTemplate(os.Stdout, "T2", sweaters) //可以选取模板
CheckErr(err)
fmt.Println("")
fmt.Println(tmpl.Name())
tmpl = tmpl.Lookup("test") //切换模板,必须要有返回,否则不生效
fmt.Println(tmpl.Name())
}输出:
17 items are made of
test
test
输出到指定文件
示例
package main
import (
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
type Inventory struct {
Material string
Count uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
CheckErr(err)
file, err := os.OpenFile("demo.txt", os.O_CREATE|os.O_WRONLY, 0755)
CheckErr(err)
err = tmpl.Execute(file, sweaters)
CheckErr(err)
}打开文件 demo.txt,内容为: 17 items are made of wool
循环操作
使用方法
{{range .Field}}
{{.ChildFieldOne}} -- {{.ChildFieldTwo }}
{{ end }}
示例
package main
import (
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
type Inventory struct {
Material string
Count uint
}
type NewInventory struct {
Fields []Inventory
}
sweaters := NewInventory{
Fields: []Inventory{
Inventory{Material: "wool", Count: 19},
Inventory{Material: "wooltwo", Count: 20},
}}
var Text = `
{{range .Fields }}
Material: {{.Material}} - Count:{{.Count}}
{{ end }}
`
tmpl, err := template.New("test").Parse(Text)
CheckErr(err)
err = tmpl.Execute(os.Stdout, sweaters)
CheckErr(err)
}模板函数
即:可以对某个字段使用函数操作。适用于稍微复杂的字段处理。
type FuncMap map[string]interface{}
t = t.Funcs(template.FuncMap{"handleFieldName": HandleFunc})
内置模板函数:
var builtins = FuncMap{
"and": and,
"call": call,
"html": HTMLEscaper,
"index": index,
"js": JSEscaper,
"len": length,
"not": not,
"or": or,
"print": fmt.Sprint,
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": URLQueryEscaper,
}示例
package main
import (
"os"
"text/template"
)
func CheckErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
type Inventory struct {
Material string
Count uint
}
type NewInventory struct {
Fields []Inventory
}
sweaters := NewInventory{
Fields: []Inventory{
Inventory{Material: "wool", Count: 19},
Inventory{Material: "wooltwo", Count: 20},
}}
var Text = `
{{range .Fields }}
Material: {{.Material | handleString}} - Count:{{.Count | handleInt }}
{{ end }}
`
tmpl, err := template.New("test").Funcs(template.FuncMap{"handleString": handleString, "handleInt": handleInt}).Parse(Text)
CheckErr(err)
err = tmpl.Execute(os.Stdout, sweaters)
CheckErr(err)
}
func handleInt(number uint) uint {
return number + 10
}
func handleString(field string) string {
return " string is: " + field
}输出:
Material: string is: wool - Count:29
Material: string is: wooltwo - Count:30
Actions
注释
{{*/\* comment \*/*}}
裁剪空格
// 裁剪 content 前后的空格
{{- content -}}
// 裁剪 content 前面的空格
{{- content }}
// 裁剪 content 后面的空格
{{ content -}}
管道函数
用法1:
{{FuncName1}}
此标签将调用名称为“FuncName1”的模板函数(等同于执行“FuncName1()”,不传递任何参数)并输出其返回值。
用法2:
{{FuncName1 "参数值1" "参数值2"}}
此标签将调用“FuncName1("参数值1", "参数值2")”,并输出其返回值
用法3:
{{.Admpub|FuncName1}}
此标签将调用名称为“FuncName1”的模板函数(等同于执行“FuncName1(this.Admpub)”,将竖线“|”左边的“.Admpub”变量值作为函数参数传送)并输出其返回值。
文本输出
{{ pipeline }}
pipeline 代表的数据会产生与调用 fmt.Print 函数类似的输出,例如整数类型的 3 会转换成字符串 “3” 输出。
条件语句
{{ if pipeline }} T1 {{ end }}
{{ if pipeline }} T1 {{ else }} T0 {{ end }}
{{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }}
*// 上面的语法其实是下面的简写*
{{ if pipeline }} T1 {{ else }}{{ if pipeline }} T0 { {end }}{{ end }}
{{ if pipeline }} T1 {{ else if pipeline }} T2 {{ else }} T0 {{ end }}
循环语句
{{ range pipeline }} T1 {{ end }}
*// 这个 else 比较有意思,如果 pipeline 的长度为 0 则输出 else 中的内容*
{{ range pipeline }} T1 {{ else }} T0 {{ end }}
*// 获取容器的下标*
{{ range $index, $value := pipeline }} T1 {{ end }}
pipeline 的值必须是数组、切片、字典和通道中的一种,即可迭代类型的值,根据值的长度输出多个 T1。
define
{{ define "name" }} T {{ end }}
定义命名为 name 的模板。
template
{{ template "name" }}
{{ template "name" pipeline }}
引用命名为 name 的模板。
block
{{ block "name" pipeline }} T1 {{ end }}
block 的语义是如果有命名为 name 的模板,就引用过来执行,如果没有命名为 name 的模板,就是执行自己定义的内容。
也就是多做了一步模板是否存在的判断,根据这个结果渲染不同的内容。
with
{{ with pipeline }} T1 {{ end }}
// 如果 pipeline 是空值则输出 T0
{{ with pipeline }} T1 {{ else }} T0 {{ end }}
{{ with arg }}
. // 此时 . 就是 arg
{{ end }}
with 创建一个新的上下文环境,在此环境中的 . 与外面的 . 无关。
参数
参数的值有多种表现形式,可以求值任何类型,包括函数、指针(指针会自动间接取值到原始的值):
布尔、字符串、字符、浮点数、复数的行为和 Go 类似
关键字 nil 代表 go 语言中的 nil
字符句号 . 代表值的结果
以 $ 字符开头的变量则为变量对应的值
结构体的字段表示为 .Field,结果是 Field 的值,支持链式调用 .Field1.Field2
字典的 key 表示为 .Key 结果是 Key 对应的值
如果是结构体的方法集中的方法 .Method 结果是方法调用后返回的值(The result is the value of invoking the method with dot as the receiver)**
方法要么只有一个任意类型的返回值要么第二个返回值为 error,不能再多了,如果 error 不为 nil,会直接报错,停止模板渲染
方法调用的结果可以继续链式调用 .Field1.Key1.Method1.Field2.Key2.Method2
声明变量方法集也可以调用 $x.Method1.Field
用括号将调用分组 print (.Func1 arg1) (.Func2 arg2) 或 (.StructValuedMethod "arg").Field
变量
action 中的 pipeline 可以初始化变量存储结果,语法也很简单:
$variable = pipeline
此时,这个 action 声明了一个变量而没有产生任何输出。
range 循环可以声明两个变量:
range $index, $element := pipeline
在 if、with 和 range 中,变量的作用域拓展到 {{ end }} 所在的位置。
如果不是控制结构,声明的变量的作用域会扩展到整个模板。
例如在模板开始时声明变量:
{{ $pages := .pagination.Pages }}
{{ $current := .pagination.Current }}
在渲染开始的时候,$ 变量会被替换成 . 开头的值,例如 $pages 会被替换成 .pagenation.Pages。所以在模板间的相互引用不会传递变量,变量只在某个特定的作用域中产生作用。
函数
模板渲染时会在两个地方查找函数:
自定义的函数 map
全局函数 map,这些函数是模板内置的
自定义函数使用 func (t *Template) Funcs(funcMap FuncMap) *Template 注册。
全局函数列表:
and
返回参数之间 and 布尔操作的结果,其实就是 JavaScript 中的逻辑操作符 &&,返回第一个能转换成 false 的值,在 Go 中就是零值,如果都为 true 返回最后一个值。
tpl := "{{ and .x .y .z }}"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"x": 1,
"y": 0,
"z": 3,
})output:
0
o
逻辑操作符 ||,返回第一个能转换成 true 的值,在 Go 中就是非零值,如果都为 false 返回最后一个值。
tpl := "{{ or .x .y .z }}"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"x": 1,
"y": 0,
"z": 3,
})
output:
1
call
返回调用第一个函数参数的结果,函数必须有一个或两个回值(第二个返回值必须是 error,如果值不为 nil 会停止模板渲染)
tpl := "call: {{ call .x .y .z }} \n"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"x": func(x, y int) int { return x+y},
"y": 2,
"z": 3,
})output:
5
html
返回转义后的 HTML 字符串,这个函数不能在 html/template 中使用。
js
返回转义后的 JavaScript 字符串。
index
在第一个参数是 array、slice、map 时使用,返回对应下标的值。
index x 1 2 3 等于 x[1][2][3]。
len
返回复合类型的长度。
not
返回布尔类型参数的相反值。
等于 fmt.Sprint。
printf
等于 fmt.Sprintf。
println
等于 fmt.Sprintln。
urlquery
对字符串进行 url Query 转义,不能在 html/template 包中使用。
// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
func URLQueryEscaper(args ...interface{}) string {
return url.QueryEscape(evalArgs(args))
}从源码可以看到这个函数直接调用 url.QueryEscape 对字符串进行转义,并没有什么神秘的。
比较函数
eq: ==
ge: >=
gt: >
le: <=
lt: <
ne: !=分析两个源码:
// eq evaluates the comparison a == b || a == c || ...
func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) {
v1 := indirectInterface(arg1)
k1, err := basicKind(v1)
if err != nil {
return false, er
}
if len(arg2) == 0 {
return false, errNoComparison
}
for _, arg := range arg2 {
v2 := indirectInterface(arg)
k2, err := basicKind(v2)
if err != nil {
return false, er
}
truth := false
if k1 != k2 {
// Special case: Can compare integer values regardless of type's sign.
switch {
case k1 == intKind && k2 == uintKind:
truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
case k1 == uintKind && k2 == intKind:
truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
default:
return false, errBadComparison
}
} else {
switch k1 {
case boolKind:
truth = v1.Bool() == v2.Bool()
case complexKind:
truth = v1.Complex() == v2.Complex()
case floatKind:
truth = v1.Float() == v2.Float()
case intKind:
truth = v1.Int() == v2.Int()
case stringKind:
truth = v1.String() == v2.String()
case uintKind:
truth = v1.Uint() == v2.Uint()
default:
panic("invalid kind")
}
}
if truth {
return true, nil
}
}
return false, nil
}
// ne evaluates the comparison a != b.
func ne(arg1, arg2 reflect.Value) (bool, error) {
// != is the inverse of ==.
equal, err := eq(arg1, arg2)
return !equal, er
}eq 先判断接口类型是否相等,然后判断值是否相等,没什么特殊的地方。
ne 更是简单的调用 eq,然后取反。
ge、gt、le、lt 与 eq 类似,先判断类型,然后判断大小。
嵌套模板
下面是一个更复杂的例子:
// 加载模板
template.ParseFiles("templates/")
// 加载多个模板到一个命名空间(同一个命名空间的模块可以互相引用)
template.ParseFiles("header.tmpl", "content.tmpl", "footer.tmpl")
// must 加载失败时 panic
tmpl := template.Must(template.ParseFiles("layout.html"))
// 执行加载后的模板文件,默认执行第一个
tmpl.Execute(w, "test")
// 如果 tmpl 中有很多个模板,可以指定要执行的模板名
tmpl.ExecuteTemplate(w, "layout", "Hello world")
ExecuteTemplate 指定的名字就是模板文件中 define "name" 的 name。
参考:
https://golang.org/pkg/text/template/
边栏推荐
- Matlab tips (22) matrix analysis -- stepwise regression
- 为基础性语言摇旗呐喊
- MySQL如何将列合并?
- Illustration creating a stored procedure using Navicat for MySQL
- RK356x U-Boot研究所(命令篇)3.3 env相关命令的用法
- 顺应媒体融合趋势,中科闻歌携手美摄打造数智媒宣
- exlipse同时操作多行。比如同时在多行同列输入相同的文字
- Loss function: Diou loss handwriting implementation
- Discussion on JMeter operation principle
- 商品服务-平台属性
猜你喜欢

【精选】资源变现资讯、新闻、自媒体、博客小程序(可引流,开通流量主,带pc后台管理)

IDEA 2021.3 执行 golang 报错:RNING: undefined behavior version of Delve is too old for Go version 1.18

Idea 2021.3 golang error: rning: undefined behavior version of delve is too old for go version 1.18

navicat数据库建表是没有utf8选项。

How can I protect my private key?

Postman génère automatiquement des fragments de code Curl
![[recruitment (Guangzhou)] Chenggong Yi (Guangzhou) Net core middle and Senior Development Engineer](/img/d8/a367c26b51d9dbaf53bf4fe2a13917.png)
[recruitment (Guangzhou)] Chenggong Yi (Guangzhou) Net core middle and Senior Development Engineer

嵌入式开发:5个可能不再被禁止的C特征

Waving flags and shouting for basic language

Rk356x u-boot Institute (command section) 3.3 env related command usage
随机推荐
【招聘(广州)】成功易(广州).Net Core中高级开发工程师
JS 二维数组变一维数组的方法
Tronapi- wavefield interface - source code without encryption - can be opened in two places - interface document attached - encapsulation based on thinkphp5 - detailed guidance of the author - 21:59:3
排查问题的方法论(适用于任何多方合作中产生的问题排查)
ERROR: Cannot uninstall ‘PyYAML‘. It is a distutils installed project and thus we cannot accurately
【系统分析师之路】第五章 复盘软件工程(敏捷开发)
Loss function: Diou loss handwriting implementation
Introduction to the new source code of substrat: fix the memory leak of the mission engine of beefy, and optimize the smart contract deletion queue
WTM重大更新,多租户和单点登录
Dark horse notes - common date API
Unity脚本的基础语法(4)-访问其他游戏对象
A keepalived high availability accident made me learn it again!
Mysql根据经纬度查询半径多少以内的数据,画个圈圈查数据库
[recruitment (Guangzhou)] Chenggong Yi (Guangzhou) Net core middle and Senior Development Engineer
Motor control Clarke( α/β) Derivation of equal amplitude transformation
数字化转型道阻且长,如何迈好关键的第一步
一条查询SQL是如何执行的
How can I protect my private key?
[Select] resource realization information, news, we media, blog applet (can be drained, open traffic master, with PC background management)
随着产业互联网的发展,有关互联网的落地和应用也就变得宽阔了起来