欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang利用Template模板動(dòng)態(tài)生成文本

 更新時(shí)間:2023年09月13日 16:31:51   作者:王中陽Go  
Go語言中的Go?Template是一種用于生成文本輸出的簡單而強(qiáng)大的模板引擎,它提供了一種靈活的方式來生成各種格式的文本,下面我們就來看看具體如何使用Template實(shí)現(xiàn)動(dòng)態(tài)文本生成吧

Go語言中的Go Template是一種用于生成文本輸出的簡單而強(qiáng)大的模板引擎。它提供了一種靈活的方式來生成各種格式的文本,例如HTML、XML、JSON等。

Go Template的具有以下主要特性:

  • 簡潔易用:Go Template語法簡潔而易于理解。它使用一對(duì)雙大括號(hào)“{{}}”來標(biāo)記模板的占位符和控制結(jié)構(gòu)。這種簡單的語法使得模板的編寫和維護(hù)變得非常方便。
  • 數(shù)據(jù)驅(qū)動(dòng):Go Template支持?jǐn)?shù)據(jù)驅(qū)動(dòng)的模板生成。你可以將數(shù)據(jù)結(jié)構(gòu)傳遞給模板,并在模板中使用點(diǎn)號(hào)“.”來引用數(shù)據(jù)的字段和方法。這種數(shù)據(jù)驅(qū)動(dòng)的方式使得模板可以根據(jù)不同的數(shù)據(jù)動(dòng)態(tài)生成輸出。
  • 條件和循環(huán):Go Template提供了條件語句和循環(huán)語句,使得你可以根據(jù)條件和迭代來控制模板的輸出。你可以使用“if”、“else”、“range”等關(guān)鍵字來實(shí)現(xiàn)條件判斷和循環(huán)迭代,從而生成靈活的輸出。
  • 過濾器和函數(shù):Go Template支持過濾器和函數(shù),用于對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換和處理。你可以使用內(nèi)置的過濾器來格式化數(shù)據(jù),例如日期格式化、字符串截?cái)嗟取4送?,你還可以定義自己的函數(shù),并在模板中調(diào)用這些函數(shù)來實(shí)現(xiàn)更復(fù)雜的邏輯和操作。
  • 嵌套模板:Go Template支持模板的嵌套,允許你在一個(gè)模板中包含其他模板。這種模板的組合和嵌套機(jī)制可以幫助你構(gòu)建更大型、更復(fù)雜的模板結(jié)構(gòu),提高代碼的可重用性和可維護(hù)性。

在很多Go開發(fā)的工具,項(xiàng)目都大量的使用了template模板。例如: Helm,K8s,Prometheus,以及一些code-gen代碼生成器等等。Go template提供了一種模板機(jī)制,通過預(yù)聲明模板,傳入自定義數(shù)據(jù)來靈活的定制各種文本。

1.示例

我們通過一個(gè)示例來了解一下template的基本使用。

首先聲明一段模板

var md = `Hello,{{ . }}`

解析模板并執(zhí)行

func main() {
    tpl := template.Must(template.New("first").Parse(md))
    if err := tpl.Execute(os.Stdout, "Jack"); err != nil {
        log.Fatal(err)
    }
}
// 輸出
// Hello Jack

在上述例子中, {{ . }}前后花括號(hào)屬于分界符,template會(huì)對(duì)分界符內(nèi)的數(shù)據(jù)進(jìn)行解析填充。其中 . 代表當(dāng)前對(duì)象,這種概念在很多語言中都存在。

在main函數(shù)中,我們通過template.New創(chuàng)建一個(gè)名為"first"的template,并用此template進(jìn)行Parse解析模板。隨后,再進(jìn)行執(zhí)行:傳入io.Writer,data,template會(huì)將數(shù)據(jù)填充至解析的模板中,再輸出到傳入的io.Writer上。

我們?cè)賮砜匆粋€(gè)例子

// {{ .xxoo -}} 刪除右側(cè)的空白
var md = `個(gè)人信息:
姓名: {{ .Name }}
年齡: {{ .Age }}
愛好: {{ .Hobby -}}
`
type People struct {
    Name string
    Age  int
}
func (p People) Hobby() string {
    return "唱,跳,rap,籃球"
}
func main() {
    tpl := template.Must(template.New("first").Parse(md))
    p := People{
        Name: "Jackson",
        Age:  20,
    }
    if err := tpl.Execute(os.Stdout, p); err != nil {
        log.Fatal(err)
    }
}
// 輸出
//個(gè)人信息:
//姓名: Jackson       
//年齡: 20            
//愛好: 唱,跳,rap,籃球

Hobby屬于People的方法,所以在模板中也可以通過.進(jìn)行調(diào)用。需要注意: 不管是字段還是方法,由于template實(shí)際解析的包與當(dāng)前包不同,無論是字段還是方法必須是導(dǎo)出的。

在template中解析時(shí),它 移除了 {{ 和 }} 里面的內(nèi)容,但是留下的空白完全保持原樣。所以解析出來的時(shí)候,我們需要對(duì)空白進(jìn)行控制。YAML認(rèn)為空白是有意義的,因此管理空白變得很重要。我們可以通過-進(jìn)行控制空白。

{{- (包括添加的橫杠和空格)表示向左刪除空白, 而 -}} 表示右邊的空格應(yīng)該被去掉。

要確保-和其他命令之間有一個(gè)空格。

{{- 10 }}: "表示向左刪除空格,打印10"

{{ -10 }}: "表示打印-10"

2.流程控制

條件判斷 IF ELSE

在template中,提供了if/else的流程判斷。

我們看一下doc的定義:

{{if pipeline}} T1 {{end}}
    如果 pipeline 的值為空,則不生成輸出;
    否則,執(zhí)行T1??罩禐?false、0、任何
    nil 指針或接口值,以及
    長度為零的任何數(shù)組、切片、映射或字符串。
    點(diǎn)不受影響。
{{if pipeline}} T1 {{else}} T0 {{end}}
    如果 pipeline 的值為空,則執(zhí)行 T0;
    否則,執(zhí)行T1。點(diǎn)不受影響。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
    為了簡化 if-else 鏈的外觀,
    if 的 else 操作可以直接包含另一個(gè) if

其中pipeline命令是一個(gè)簡單的值(參數(shù))或一個(gè)函數(shù)或方法調(diào)用。我們第一個(gè)例子的hobby就屬于方法調(diào)用。

繼續(xù)是上面的案例,我們添加了一個(gè)IF/ELSE來判斷年齡,在IF中我們使用了一個(gè)內(nèi)置函數(shù)gt判斷年齡。

在template中,調(diào)用函數(shù),傳遞參數(shù)是跟在函數(shù)后面: function arg1 agr2

或者也可以通過管道符進(jìn)行傳遞:arg | function

每個(gè)函數(shù)都必須有1到2個(gè)返回值,如果有2個(gè)則后一個(gè)必須是error接口類型。

var md = `個(gè)人信息:
姓名: {{ .Name }}
年齡: {{ .Age }}
愛好: {{ .Hobby -}}
{{ if gt .Age 18 }}
成年人
{{ .Age | print }}
{{ else }}
未成年人
{{ end }}
`
// 輸出
//個(gè)人信息:
//姓名: Jackson       
//年齡: 20            
//愛好: 唱,跳,rap,籃球
//成年人              
//20

循環(huán)控制range

template同時(shí)也提供了循環(huán)控制的功能。我們還是先看一下doc

{range pipeline}} T1 {{end}} pipeline 的值必須是數(shù)組、切片、映射或通道。
    如果管道的值長度為零,則不輸出任何內(nèi)容;
    否則,將點(diǎn)設(shè)置為數(shù)組的連續(xù)元素,
    切片或映射并執(zhí)行 T1。如果值是映射并且鍵是具有定義順序的基本類型,則將按排序鍵順序訪問
    
{{range pipeline}} T1 {{else}} T0 {{end}} 
    pipeline 的值必須是數(shù)組、切片、映射或通道。
    如果管道的值長度為零,則 . 不受影響并
    執(zhí)行 T0;否則,將 . 設(shè)置為數(shù)組、切片或映射的連續(xù)元素,并執(zhí)行 T1。
    
{{break}}
    最里面的 {{range pipeline}} 循環(huán)提前結(jié)束,停止當(dāng)前迭代并繞過所有剩余迭代。
    
{{continue}}
    最里面的 {{range pipeline}} 循環(huán)的跳過當(dāng)前迭代

整合上面的IF/ELSE,我們做一個(gè)綜合案例

var md = `
Start iteration:
{{- range . }}
{{- if gt . 3 }}
超過3
{{- else }}
{{ . }}
{{- end }}
{{ end }}
`
func main() {
    tpl := template.Must(template.New("first").Parse(md))
    p := []int{1, 2, 3, 4, 5, 6}
    if err := tpl.Execute(os.Stdout, p); err != nil {
        log.Fatal(err)
    }
}
// 輸出
//1       
//2        
//3       
//超過3    
//超過3    
//超過3

我們通過{{ range . }}遍歷傳入的對(duì)象,在循環(huán)內(nèi)部再通過{{ if }}/{{ else }}判斷每個(gè)元素的大小。

作用域控制with

在語言中都有一個(gè)作用域的概念。template也提供了通過使用with去修改作用域。

我們來看一個(gè)案例

var md = `
people name(out scope): {{ .Name }}
dog name(out scope): {{ .MyDog.Name }}
{{- with .MyDog }}
dog name(in scope): {{ .Name }} 
people name(in scope): {{ $.Name }}
{{ end }}
`
type People struct {
    Name  string
    Age   int
    MyDog Dog
}
type Dog struct {
    Name string
}
func main() {
    tpl := template.Must(template.New("first").Parse(md))
    p := People{Name: "Lucy", MyDog: Dog{Name: "Tom"}}
    if err := tpl.Execute(os.Stdout, p); err != nil {
        log.Fatal(err)
    }
}
// 輸出
//people name(out scope): Lucy
//dog name(out scope): Tom    
//dog name(in scope): Tom     
//people name(in scope): Lucy

在頂層作用域中,我們直接可以通過.去獲取對(duì)象的信息。在聲明的with中,我們將頂層對(duì)象的MyDog傳入,那么在with作用域中,通過.獲取的對(duì)象就是Dog。所以在with中我們可以直接通過.獲取Dog的name。

有些時(shí)候,在子作用域中我們可能也希望可以獲取到頂層對(duì)象,那么我們可以通過$獲取頂層對(duì)象。上述例子的$.獲取到People。

3.函數(shù)

在第二節(jié)內(nèi)容中,我們使用了print,gt函數(shù),這些函數(shù)都是預(yù)定義在template中。我們通過查閱源碼可以查看預(yù)定義了以下函數(shù):

func builtins() FuncMap {
    return FuncMap{
        "and":      and,
        "call":     call,
        "html":     HTMLEscaper,
        "index":    index,
        "slice":    slice,
        "js":       JSEscaper,
        "len":      length,
        "not":      not,
        "or":       or,
        "print":    fmt.Sprint,
        "printf":   fmt.Sprintf,
        "println":  fmt.Sprintln,
        "urlquery": URLQueryEscaper,
        // Comparisons
        "eq": eq, // ==
        "ge": ge, // >=
        "gt": gt, // >
        "le": le, // <=
        "lt": lt, // <
        "ne": ne, // !=
    }
}

在實(shí)際開發(fā)中,僅僅是這些函數(shù)是很難滿足我們的需求。此時(shí),我們希望能夠傳入自定義函數(shù),在我們編寫模板的時(shí)候可以使用自定義的函數(shù)。

我們引入一個(gè)需求: 希望將傳入的str可以轉(zhuǎn)為小寫。

var md = `
result: {{ . | lower }}
`
func Lower(str string) string {
    return strings.ToLower(str)
}
func main() {
    tpl := template.Must(template.New("demo").Funcs(map[string]any{
        "lower": Lower,
    }).Parse(md))
    tpl.Execute(os.Stdout, "HELLO FOSHAN")
}
// 輸出
// result: hello foshan

由于template支持鏈?zhǔn)秸{(diào)用,所以我們一般把Parse放在最后

我們通過調(diào)用Funcs,傳入functionName : function的map。

執(zhí)行模板時(shí),函數(shù)從兩個(gè)函數(shù)map中查找:首先是模板函數(shù)map,然后是全局函數(shù)map。一般不在模板內(nèi)定義函數(shù),而是使用Funcs方法添加函數(shù)到模板里。

方法必須有一到兩個(gè)返回值,如果是兩個(gè),那么第二個(gè)一定是error接口類型

注意:Funcs必須在解析parse前調(diào)用。如果模板已經(jīng)解析了,再傳入funcs,template并不知道該函數(shù)應(yīng)該如何映射。

4.變量

函數(shù)、管道符、對(duì)象和控制結(jié)構(gòu)都可以控制,我們轉(zhuǎn)向很多編程語言中更基本的思想之一:變量。 在模板中,很少被使用。但是我們可以使用變量簡化代碼,并更好地使用withrange。

我們通過{{ $var := .Obj }}聲明變量,在with/range中我們使用的會(huì)比較頻繁

var md = `
{{- $count := len . -}}
共有{{ $count }}個(gè)元素
{{- range $k,$v := . }}
{{ $k }} => {{ $v }}
{{- end }}
`
func main() {
    tpl := template.Must(template.New("demo").Parse(md))
    tpl.Execute(os.Stdout, map[string]string{
        "p1": "Jack",
        "p2": "Tom",
        "p3": "Lucy",
    })
}
// 輸出
// 共有3個(gè)元素
// p1 => Jack 
// p2 => Tom  
// p3 => Lucy

{{ var }}聲明的變量也有作用域的概念,如果在頂層作用域中聲明了var,那么在內(nèi)部作用域可以直接通過獲取該變量

我們通過{{- range $k,$v := . }}遍歷map中每一個(gè)KV,這種寫法類似于Golang的for-range。

5.命名模板

在Go語言的模板引擎中,命名模板是指通過給模板賦予一個(gè)唯一的名稱,將其存儲(chǔ)在模板集中,以便后續(xù)可以通過該名稱來引用和執(zhí)行該模板。

通過使用命名模板,你可以將一組相關(guān)的模板邏輯組織在一起,并在需要的時(shí)候方便地調(diào)用和重用它們。這對(duì)于構(gòu)建復(fù)雜的模板結(jié)構(gòu)和提高模板的可維護(hù)性非常有用。

在編寫復(fù)雜模板的時(shí)候,我們總是希望可以抽象出公用模板,那么此時(shí)就需要使用命名模板進(jìn)行復(fù)用。

本節(jié)將基于K8sPod模板的案例來學(xué)習(xí)如何使用命名模板進(jìn)行抽象復(fù)用。

我們看一下doc

{{template "name"}}
    具有指定名稱的模板以無數(shù)據(jù)執(zhí)行。
{{template "name" pipeline}}
    具有指定名稱的模板以pipeline結(jié)果執(zhí)行。

通過define定義模板名稱

{{ define "container" }}
    模板
{{ end }}

通過template使用模板

{{ template "container" }}

我們?cè)谑褂胻emplate.New傳入的name,實(shí)際上就是定義了模板的名稱

案例:我們希望抽象出Pod的container,通過代碼來傳入數(shù)據(jù)生成container,避免重復(fù)的編寫yaml。

var pod = `
apiVersion: v1
kind: Pod
metadata:
  name: "test"
spec:
  containers:
{{- template "container" .}}
`
var container = `
{{ define "container" }}
    - name: {{ .Name }}
      image: "{{ .Image}}"
{{ end }}
`
func main() {
    tpl := template.Must(template.New("demo").Parse(pod))
    tpl.Parse(container)
    tpl.ExecuteTemplate(os.Stdout, "demo", struct {
        Name  string
        Image string
    }{
        "nginx",
        "1.14.1",
    })
}
// 輸出
apiVersion: v1
kind: Pod
metadata:
  name: "test"
spec:
  containers:
    - name: nginx    
      image: "1.14.1"

tpl可以解析多個(gè)模板,在不同模板中通過define定義模板即可。使用ExecuteTemplate傳入模板名指定解析模板。在{{- template "container" .}}中可以傳入對(duì)象數(shù)據(jù)。

在實(shí)際開發(fā)中,我們往往不會(huì)采用打印的方式輸出??梢愿鶕?jù)不同的需求,在Execute執(zhí)行時(shí)選擇不同的io.Writer。往往我們更希望寫入到文件中。

6.Template常用函數(shù)

func Must(t *Template, err error) *Template

Must是一個(gè)helper函數(shù),它封裝對(duì)返回(Template, error)的函數(shù)的調(diào)用,并在錯(cuò)誤非nil時(shí)panic。它旨在用于template初始化。

// 解析指定文件
// 示例: ParseFiles(./pod.tpl) 
func ParseFiles(filenames ...string) (*Template, error)
// 解析filepath.Match匹配文件
// 示例: ParseGlob(/data/*.tpl)
func ParseGlob(pattern string) (*Template, error)

這兩個(gè)函數(shù)幫助我們解析文件中的模板,大多數(shù)情況下我們都是將模板寫在.tpl結(jié)尾的文件中。通過不同的解析規(guī)則解析對(duì)應(yīng)的文件。

func (t *Template) Templates() []*Template

返回當(dāng)前t相關(guān)的模板的slice,包括t本身。

func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) error

傳入模板名稱,執(zhí)行指定的模板。

如果在執(zhí)行模板或?qū)懭肫漭敵鰰r(shí)發(fā)生錯(cuò)誤,執(zhí)行將停止,但部分結(jié)果可能已經(jīng)被寫入輸出寫入器。模板可以安全地并行執(zhí)行,但如果并行執(zhí)行共享一個(gè)Writer,則輸出可能交錯(cuò)。

func (t *Template) Delims(left, right string) *Template

修改模板中的分界符,可以將{{}}修改為<>

func (t *Template) Clone() (*Template, error)

clone返回模板的副本,包括所有關(guān)聯(lián)模板。在clone的副本上添加模板是不會(huì)影響原始模板的。所以我們可以將其用于公共模板,通過clone獲取不同的副本。

7.總結(jié)

Golang的template提高代碼重用性:模板引擎允許你創(chuàng)建可重用的模板片段。通過將重復(fù)的模板邏輯提取到單獨(dú)的模板中,并在需要時(shí)進(jìn)行調(diào)用,可以減少代碼重復(fù),提高代碼的可維護(hù)性和可擴(kuò)展性。有許多code-gen使用了template + cobra方式生成復(fù)用代碼和模板代碼,有利于我們解放雙手。

以上就是Golang利用Template模板動(dòng)態(tài)生成文本的詳細(xì)內(nèi)容,更多關(guān)于Go Template的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 利用Go語言實(shí)現(xiàn)簡單Ping過程的方法

    利用Go語言實(shí)現(xiàn)簡單Ping過程的方法

    相信利用各種語言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過程,而對(duì)Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個(gè)ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計(jì)。
    2016-09-09
  • Golang通道channel的源碼分析

    Golang通道channel的源碼分析

    channel(通道),顧名思義,是一種通道,一種用于并發(fā)環(huán)境中數(shù)據(jù)傳遞的通道。channel是golang中標(biāo)志性的概念之一,很好很強(qiáng)大!本文將從源碼帶大家了解一下channel的使用,希望對(duì)大家有所幫助
    2022-12-12
  • golang中的三個(gè)點(diǎn) ''...''的用法示例詳解

    golang中的三個(gè)點(diǎn) ''...''的用法示例詳解

    這篇文章主要介紹了golang中的三個(gè)點(diǎn) '...' 的用法示例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Golang哈希算法實(shí)現(xiàn)配置文件的監(jiān)控功能詳解

    Golang哈希算法實(shí)現(xiàn)配置文件的監(jiān)控功能詳解

    這篇文章主要介紹了Golang哈希算法實(shí)現(xiàn)配置文件的監(jiān)控功能,哈希和加密類似,唯一區(qū)別是哈希是單項(xiàng)的,即哈希后的數(shù)據(jù)無法解密,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-03-03
  • vscode配置go開發(fā)環(huán)境的實(shí)戰(zhàn)過程

    vscode配置go開發(fā)環(huán)境的實(shí)戰(zhàn)過程

    vscode配置go的開發(fā)環(huán)境很簡單,下面這篇文章主要給大家介紹了關(guān)于vscode配置go開發(fā)環(huán)境的實(shí)戰(zhàn)過程,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • 10個(gè)現(xiàn)代網(wǎng)站開發(fā)必備的Go軟件包工具盤點(diǎn)

    10個(gè)現(xiàn)代網(wǎng)站開發(fā)必備的Go軟件包工具盤點(diǎn)

    這篇文章主要為大家介紹了10個(gè)現(xiàn)代網(wǎng)站開發(fā)必備的Go軟件包,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 詳解Go channel管道的運(yùn)行原理

    詳解Go channel管道的運(yùn)行原理

    Go推薦通過通信來共享內(nèi)存,而channel就實(shí)現(xiàn)了這一理念。那channel是怎么運(yùn)行的呢?本文將帶你搞懂Go channel管道的運(yùn)行原理,感興趣的同學(xué)可以參考一下
    2023-05-05
  • Golang基礎(chǔ)常識(shí)性面試中常見的六大陷阱及應(yīng)對(duì)技巧總結(jié)

    Golang基礎(chǔ)常識(shí)性面試中常見的六大陷阱及應(yīng)對(duì)技巧總結(jié)

    Go是一門簡單有趣的語言,但與其他語言類似,它會(huì)有一些技巧,這篇文章主要給大家介紹了關(guān)于Golang基礎(chǔ)常識(shí)性面試中常見的六大陷阱及應(yīng)對(duì)技巧的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-08-08
  • 總結(jié)Golang四種不同的參數(shù)配置方式

    總結(jié)Golang四種不同的參數(shù)配置方式

    這篇文章主要介紹了總結(jié)Golang四種不同的參數(shù)配置方式,文章圍繞主題展開詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • 一文徹底理解Golang閉包實(shí)現(xiàn)原理

    一文徹底理解Golang閉包實(shí)現(xiàn)原理

    閉包對(duì)于一個(gè)長期寫Java的開發(fā)者來說估計(jì)鮮有耳聞,光這名字感覺就有點(diǎn)"神秘莫測(cè)"。這篇文章的主要目的就是從編譯器的角度來分析閉包,徹底搞懂閉包的實(shí)現(xiàn)原理,需要的可以參考一下
    2022-10-10

最新評(píng)論