总览

作为软件开发人员,文本无处不在。 代码是文本,HTML是文本,XNL / JSON / YAML / TOML是文本,Markdown是文本,CSV是文本。 所有这些文本格式旨在满足人类和机器的需求。 人类应该能够使用纯文本编辑器阅读和编辑文本格式。

但是在很多情况下,您需要以某种格式生成文本。 您可以从一种格式转换为另一种格式,创建自己的DSL,自动生成一些帮助程序代码,或者仅使用用户特定信息定制电子邮件。 不管有什么需要,Go凭借其强大的模板都可以在整个过程中为您提供帮助。

在本教程中,您将了解Go模板的来龙去脉,以及如何使用它们进行强大的文本生成。

什么是Go模板?

Go模板是使用特殊的占位符(称为动作)来管理某些文本的对象,这些占位符用双花括号括起来: {{ some action }} 。 执行模板时,将为其提供Go结构,该结构具有占位符所需的数据。

这是一个产生敲打笑话的简单示例。 敲门笑话的格式很严格。 唯一改变的是门环和打Kong线的身份。

package mainimport ("text/template""os"
)type Joke struct {Who stringPunchline string
}func main() {t := template.New("Knock Knock Joke")text := `Knock Knock\nWho's there?{{.Who}}{{.Who}} who?{{.Punchline}}`t.Parse(text)jokes := []Joke{{"Etch", "Bless you!"},{"Cow goes", "No, cow goes moo!"},}for _, joke := range jokes {t.Execute(os.Stdout, joke)}
}Output:Knock Knock
Who's there?
Etch
Etch who?
Bless you!Knock Knock
Who's there?
Cow goes
Cow goes who?
No, cow goes moo!

了解模板动作

模板语法非常强大,它支持诸如数据访问器,函数,管道,变量,条件和循环之类的操作。

资料存取器

数据访问器非常简单。 他们只是将数据从struct开始。 他们也可以深入嵌套结构:

func main() {family := Family{Father: Person{"Tarzan"},Mother: Person{"Jane"},ChildrenCount: 2,}t := template.New("Father")text := "The father's name is {{.Father.Name}}"t.Parse(text)t.Execute(os.Stdout, family)
}

如果数据不是结构,则可以仅使用{{.}}直接访问该值:

func main() {   t := template.New("")t.Parse("Anything goes: {{.}}\n")t.Execute(os.Stdout, 1)t.Execute(os.Stdout, "two")t.Execute(os.Stdout, 3.0)t.Execute(os.Stdout, map[string]int{"four": 4})
}Output:Anything goes: 1
Anything goes: two
Anything goes: 3
Anything goes: map[four:4]

稍后我们将看到如何处理数组,切片和映射。

功能

功能确实提升了模板的功能。 全局函数很多,甚至可以添加特定于模板的函数。 可以在Go网站上找到全局功能的完整列表。

这是如何在模板中使用printf函数的示例:

func main() {t := template.New("")t.Parse(`Keeping just 2 decimals of π: {{printf "%.2f" .}}`)t.Execute(os.Stdout, math.Pi)
}Output:Keeping just 2 decimals of π: 3.14

流水线

管道使您可以将多个函数应用于当前值。 组合不同的功能会大大扩展您将值切片和切块的方式。

在以下代码中,我链接了三个函数。 首先,调用函数执行传递给Execute()的函数。 然后len函数返回输入函数结果的长度,在这种情况下为3。 最后, printf函数将打印项目数。

func main() {t := template.New("")t.Parse(`{{ call . | len | printf "%d items" }}`)t.Execute(os.Stdout, func() string { return "abc" })
}Output:3 items

变数

有时您想多次重用复杂管道的结果。 使用Go模板,您可以定义一个变量并根据需要重复使用它多次。 下面的示例从输入结构中提取名字和姓氏,并用引号将它们存储在变量$F$L 。 然后,它以正常和相反的顺序渲染它们。

另一个巧妙的技巧是,我将匿名结构传递给模板,以使代码更简洁,并避免仅在一个地方使用的类型使代码混乱。

func main() {t := template.New("")t.Parse(`{{ $F := .FirstName | printf "%q"}}{{ $L := .LastName  | printf "%q"}}Normal:  {{$F}} {{$L}}Reverse: {{$L}} {{$F}}`)t.Execute(os.Stdout, struct {FirstName stringLastName  string}{"Gigi","Sayfan",})
}Output:Normal:  "Gigi" "Sayfan"
Reverse: "Sayfan" "Gigi"

有条件的

但是,我们不要在这里停下来。 您甚至可以在模板中包含条件。 有一个if-end操作和if-else-end操作。 如果条件管道的输出不为空,则显示if子句:

func main() {t := template.New("")t.Parse(`{{ if . -}} {{ . }} {{ else }} No data is available {{ end }}`)t.Execute(os.Stdout, "42")t.Execute(os.Stdout, "")
}Output:42 No data is available

请注意,else子句引起换行,并且“无可用数据”文本明显缩进。

循环

Go模板也有循环。 当您的数据包含切片,地图或其他可迭代对象时,此功能非常有用。 循环的数据对象可以是任何可迭代的Go对象,例如数组,切片,映射或通道。 范围函数使您可以遍历数据对象并为每个元素创建一个输出。 让我们看看如何遍历地图:

func main() {t := template.New("")e := `Name,Scores{{range $k, $v := .}}{{$k}}{{range $s := $v}},{{$s}}{{end}}{{end}}`t.Parse(e)t.Execute(os.Stdout, map[string][]int{"Mike":  {88, 77, 99},"Betty": {54, 96, 78},"Jake":  {89, 67, 93},})
}Output:Name,ScoresBetty,54,96,78Jake,89,67,93Mike,88,77,99

如您所见,前导空格仍然是一个问题。 我无法在模板语法中找到合适的方法来解决它。 这将需要后处理。 从理论上讲,您可以在前面或后面的动作中放置一个破折号以修剪空白,但是在range的作用下它不起作用。

文字范本

文本模板在text / template包中实现 。 除了到目前为止我们已经看到的所有内容之外,该程序包还可以从文件加载模板,并使用template动作组成多个模板。 Template对象本身具有许多支持此类高级用例的方法:

  • ParseFiles()
  • ParseGlob()
  • AddParseTree()
  • 克隆()
  • DefinedTemplates()
  • Delims()
  • ExecuteTemplate()
  • Funcs()
  • 抬头()
  • 选项()
  • 模板()

由于篇幅所限,我将不再进一步详细介绍(也许在其他教程中)。

HTML模板

HTML模板在html / template包中定义。 它具有与文本模板包完全相同的界面,但其设计目的是生成可防止代码注入HTML。 通过在将数据嵌入模板之前仔细清除数据来完成此操作。 可行的假设是模板作者是受信任的,但是提供给模板的数据是不可信的。

这个很重要。 如果您自动应用从不受信任的来源收到的模板,那么html / template包将无法保护您。 您有责任检查模板。

让我们看看text/templatehtml/template的输出之间的区别。 使用文本/模板时,很容易将JavaScript代码注入到生成的输出中。

package mainimport ("text/template""os"
)func main() {t, _ := template.New("").Parse("Hello, {{.}}!")d := "<script>alert('pawned!')</script>"t.Execute(os.Stdout, d)
}Output:Hello, <script>alert('pawned!')</script>!

但是,通过转义脚本标签和括号,导入html/template而不是text/template可以防止此攻击:

Hello, &lt;script&gt;alert('pwened!')&lt;/script&gt;!

处理错误

错误有两种:解析错误和执行错误。 Parse()函数解析模板文本并返回错误,我在代码示例中忽略了该错误,但是在生产代码中,您希望尽早捕获这些错误并加以解决。

如果您想要快速而肮脏的退出,那么Must()方法将获取返回(*Template, error)的方法的输出,例如Clone()Parse()ParseFiles() ,如果错误不是此错误,则会恐慌零。 这是检查显式解析错误的方法:

func main() {e := "I'm a bad template, }}{{"_, err := template.New("").Parse(e)if err != nil {msg := "Failed to parsing: '%s'.\nError: %v\n"fmt.Printf(msg, e, err)}
}Output:Failed to parse template: 'I'm a bad template, }}{{'.
Error: template: :1: unexpected unclosed action in command

如果模板有问题,使用Must()只会恐慌:

func main() {e := "I'm a bad template, }}{{"template.Must(template.New("").Parse(e))
}Output:panic: template: :1: unexpected unclosed action in command

如果提供的数据与模板不匹配,则另一种错误是执行错误。 同样,您可以显式检查或使用Must()惊慌。 在这种情况下,我建议您检查并建立恢复机制。

通常,无需仅由于输入不满足要求而使整个系统瘫痪。 在下面的示例中,模板在数据结构上期望一个名为Name的字段,但是我为结构提供了一个名为FullName的字段。

func main() {e := "There must be a name: {{.Name}}"t, _ := template.New("").Parse(e)err := t.Execute(os.Stdout,struct{ FullName string }{"Gigi Sayfan"},)if err != nil {fmt.Println("Fail to execute.", err)}
}Output:There must be a name: Fail to execute.
template: :1:24: executing "" at <.Name>:
can't evaluate field Name in type struct { FullName string }

结论

Go具有强大而完善的模板系统。 在Kubernetes和Hugo等许多大型项目中,它的使用效果非常好。 html / template软件包提供了一种安全的,具有工业实力的设施,可以对基于Web的系统的输出进行清理。 在本教程中,我们介绍了所有基础知识和一些中间用例。

模板包中还有更多高级功能正在等待解锁。 玩模板并将其合并到您的程序中。 您将惊喜地发现您的文本生成代码看起来多么简洁和可读。

翻译自: https://code.tutsplus.com/tutorials/text-generation-with-go-templates--cms-30441

使用Go模板生成文本相关推荐

  1. JAVA 使用Itext模板生成pdf,解决图片插入,文本域超出字体缩放,半自动换行

    1.前言 前一段时间遇到一个制作Pdf的业务,自己下来摸索了一下,基本上解决.将其中遇到的几个问题及解决方法做以记录,仅供大家参考. 首先在这里对于刚接触该类型业务的同学说明下,ItexPdf支持使用 ...

  2. 如何使用word模板生成word文档(文本,图片)

    注意:只针对数据信息与图片信息进行生成. 一,准备工作 1,编辑word模板,变量信息以${变量名称}表示,图片要使用临时图片占用位置. 2,转换格式word输出为Word.xml文档格式,在手动改为 ...

  3. NLP实战:利用Python理解、分析和生成文本 | 赠书

    导读:本文内容参考自<自然语言处理实战:利用Python理解.分析和生成文本>一书,由Hobson Lane等人所著. 本书是介绍自然语言处理(NLP)和深度学习的实战书.NLP已成为深度 ...

  4. 创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]

    在<上篇>中我们通过T4模板为我们指定的数据表成功生成了我们需要的用于添加.修改和删除操作的存储过程.但是这是一种基于单个文件的解决方案,即我们必须为每一个生成的存储过程建立一个模板.如果 ...

  5. [转]MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    本文转自:http://www.cnblogs.com/guomingfeng/p/mvc-ef-t4.html 〇.目录 一.前言 二.工具准备 三.T4代码生成预热 (一) 单文件生成:Hello ...

  6. csp真题字符串匹配c语言,CCF CSP认证考试历年真题 模板生成系统 C语言实现

    试题编号:201509-3 试题名称:日期计算 时间限制:1.0s 内存限制:256.0MB 问题描述: 成成最近在搭建一个网站,其中一些页面的部分内容来自数据库中不同的数据记录,但是页面的基本结构是 ...

  7. word模板生成word报表文档

    主要功能为根据word模板生成word报表文档,注意引用Interop.Word.dll; 首先要生成word程序对象 Word.Application app = new Word.Applicat ...

  8. 根据标准word模板生成word文档类库(开源)

    前言   最近因项目需要要自定义标准word模板,并以编码方式操作word模板.填充数据和生成word文档,于是自己写了条小"内裤"来实现这个功能.该"内裤"只 ...

  9. C#根据word模板生成word表格报表文档

    主要功能为根据word模板生成word报表文档,注意引用Interop.Word.dll; 首先要生成word程序对象 Word.Application app = new Word.Applicat ...

最新文章

  1. 谷歌宣布即将开放 .dev 顶级域名注册
  2. 企业管理,难的是什么?
  3. android 中测量高度和宽度,android获得屏幕高度和宽度(display中getSize(Point)方法使用)...
  4. 检测动态生成的单选按钮和jQuery的变化
  5. 利用Linq在RadCombobox中输出分类后的数据
  6. lfw2019_来自 LFW SS20 你应该知道的5个时尚趋势
  7. IO模型(epoll)--详解-02
  8. c++ map 获取key列表_好未来Golang源码系列一:Map实现原理分析
  9. python语言三大基本控制结构_Python基础(4) 控制结构
  10. python刷题相关资料汇总(一)
  11. linux 提示库文件,Linux系统下确实库文件的解决办法
  12. iis7 php 中文乱码,php输出文字乱码的解决方法
  13. 汉英词典python
  14. 微信的商业价值有哪些?
  15. 库存管理系统的设计与实现(代码)
  16. 我的计算机梦想作文,我的梦想作文600字
  17. 壳与加壳脱壳基础知识
  18. 文件实现输入三行hello,实现在每个hello后面换行
  19. 2018中国智造金长城奖:创新能力与行业竞争力并重
  20. Vue 中 props 传值,父组件向子组件传递对象/数组可以直接修改的问题

热门文章

  1. 分布式系统慎用@Transactional注解
  2. 关于——GitHub注册问题详细解决步骤
  3. qiankun 部署微前端-vue2 (二)
  4. Java常用的序列化框架
  5. fm2018 ajax,fm2018各位置球员排行 各位置神级球员能力值排名
  6. Git使用小技巧【修改commit注释, 超详细】
  7. 中级职称考试有哪些科目
  8. 性能优化的核心思路,干货分享
  9. 被抽中PMP的审查,该怎么通过?
  10. idea数据库表关联