1. 程式人生 > >(譯)golang文本模板

(譯)golang文本模板

字母數 程序 浮點 glob val 全局變量 之一 ddp 多參數

概述

template包實現了用於生成文本輸出的數據驅動(data-driven)模板。

要生成HTML輸出,請參閱html / template 包,它與此包具有相同的接口,但會自動保護HTML輸出免受某些攻擊。

通過將模板應用於一個數據結構來執行模板。模板中的標記引用該數據結構的元素(通常是struct中的字段或map中的鍵)來控制執行和獲取要顯示的值。模板的執行遍歷該數據結構並設置遊標,以‘.‘表示,指向作為執行過程中該數據結構當前位置的值。

模板的輸入文本是任何格式的UTF-8編碼文本。 "動作(Actions)"用於執行數據求值或控制結構,位於 "{{" 和 "}}" 中。所有操作以外的文本都將被復制到輸出中。除了原始字符串,操作通常不換行,盡管註釋可以。

一旦解析,模板可以並行安全地執行,但如果並行執行共享同一個Writer,則輸出可能會交錯。

這是一個簡單的例子,打印 "17 items are made of wool" 。

type Inventory struct {
    Material string
    Count    uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }

下面顯示更復雜的例子。

文本和空格

默認情況下,在執行模板時,操作之間的所有文本都會逐字復制。 例如,在程序運行時,上述示例中的字符串 "items are made of" 出現在標準輸出中。

然而,為了幫助格式化模板源代碼,如果操作的左分隔符(默認為"{{")後面緊跟著一個減號和ASCII空格字符("{{- "),則前面的文本中的所有尾部空白符將被丟棄。 同樣,如果右分隔符("}}")前面有空格和減號(" -}}"),則所有前導空格都將從緊隨其後的文本中刪除。 在這些修剪標記中,ASCII空格必須存在,否則,如 "{{-3}}" 將被解析為包含數字 -3 的操作。

例如,當執行源代碼為的模板時

"{{23 -}} < {{- 45}}"

生成的輸出為

"23<45"

對於這種修剪,空白字符的定義與Go語言相同,包括:空格,水平制表符,回車符和換行符。

動作

下面是動作的列表,"Arguments"(參數) 和 "pipelines" (管道)是數據計算,在後面的相應部分詳細說明。

{{/* a comment */}}
    註釋,被丟棄。

{{pipeline}}
    管道的默認文本表示(與fmt.Print相同)將復制到輸出中。

{{if pipeline}} T1 {{end}}
    如果管道是空值,不產生輸出。
    否則,輸出T1。空值包括false, 0, 值為nil的指針和接口值, 長度為0的array, slice, map,字符串。
    不影響遊標。

{{if pipeline}} T1 {{else}} T0 {{end}}
    如果管道是空值, 輸出T0。
    否則,輸出T1。不影響遊標。

{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
    簡化if-else鏈, 當else操作直接包括一個if操作時,效果等同於:
        {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}

{{range pipeline}} T1 {{end}}
    管道的值必須是一個array, slice, map, 或channel。
    如果管道的值的長度為0,不輸出。
    否則,遊標連續設置為array,slice,map的元素並輸出T1。如果是值是map,而且其鍵為基本類型並存在自然順序,將按鍵的排序順序遍歷元素。

{{range pipeline}} T1 {{else}} T0 {{end}}
    管道的值必須是一個array, slice, map, 或channel。
    如果pipeline的長度為0,不影響遊標且輸出T0;否則,遊標連續設置為array,slice,map的元素並輸出T1。

{{template "name"}}
    指定名稱的模板將應用nil數據的方式執行。

{{template "name" pipeline}}
    指定名稱的模板將應用遊標為管道的方式執行。

{{block "name" pipeline}} T1 {{end}}
    塊是用來定義並執行模板的簡寫形式:
        {{define "name"}} T1 {{end}}
        {{template "name" pipeline}}
    典型的用途是定義一組根模板,然後通過重新定義其中的塊模板從而自定義。

{{with pipeline}} T1 {{end}}
    如果管道的值為空,則不會生成輸出; 否則,遊標被設置為管道的值並且T1被執行。

{{with pipeline}} T1 {{else}} T0 {{end}}
    如果管道的值為空,則遊標不受影響並且T0被執行; 否則,dot被設置為管道的值並執行T1。

參數

參數是一個簡單的值,表示為以下之一。

- 一個Go語言中的布爾值、字符串、字符、整數、浮點數、虛數、復數常量。 這些行為就像Go的未定義類型常量一樣。
- nil,表示Go中的未定義類型nil。
- '.',結果為遊標值。
- 變量名稱, 跟在$後的數字字母組成的字符串(可能為空), 如
    $piOver2
  或
    $
  結果為變量值。
  
  變量描述如下。
- struct字段名, 前面有一個“.”,如:
    .Field
  結果為字段值。可以鏈式調用字段:
    .Field1.Field2
  還可以對變量進行字段計算,包括鏈接:
    $x.Field1.Field2
- map的鍵名,前面有一個“.”,如:
    .Key
  結果為key對應的value值。
  鍵調用可以鏈接並與字段組合任意深度:
    .Field1.Key1.Field2.Key2
  盡管key必須是字母數字標識符,但與字段名稱不同,不需要以大寫字母開頭。
??鍵也可以在變量上進行計算,包括鏈接:
    $x.key1.key2
- 數據的方法名,前面有一個“.”,如:
    .Method
  結果為以遊標為接收者執行該方法的返回值, dot.Method()。方法必須返回一個(可以是任意類型)或兩個值, 第二個值為err。
  如err不為nil, 執行中斷,並將err作為執行的返回值,返回給調用者。
  方法調用可以鏈接並與字段、鍵組成任意深度:
    .Field1.Key1.Method1.Field2.Key2.Method2
  鍵也可以在變量上進行計算,包括鏈接:
    $x.Method1.Field
- 函數名,如:
    fun
  結果為執行函數返回值, fun()。返回值表現與方法中相同。函數和函數名描述如下:
- 括號用於分組。結果參與字段或映射鍵調用。
    print (.F1 arg1) (.F2 arg2)
    (.StructValuedMethod "arg").Field

參數可以計算為任何類型; 如果它們是指針,則實現會在需要時自動指向基本類型。 如果評估產生一個函數值,例如結構體的函數值域,該函數不會自動調用,但它可以用作if操作等的真值。 要調用它,請使用下面定義的調用函數。

管道

管道是一個可能鏈接的 “命令” 序列。 一個命令是一個簡單的值(參數)或一個函數或方法調用,可能有多個參數。

Argument
    參數的評估值。
.Method [Argument...]
    該方法可以是單獨的,也可以是鏈中的最後一個元素,但與鏈中間的方法不同,它可能需要參數。 結果是使用參數調用方法的值:
        dot.Method(Argument1, etc.)
functionName [Argument...]
    結果是調用該名稱函數的值:
        function(Argument1, etc.)
    函數和函數名稱如下所述。

管道可以通過用管道符‘|‘分隔一系列命令來“鏈接”。 在鏈式管道中,每個命令的結果都作為後續命令的最後一個參數傳遞。 管道中最終命令的輸出是管道的值。

命令的輸出可以是一個值或兩個值,其中第二個具有類型錯誤。 如果第二個值存在並且評估為非零,則執行終止,並將錯誤返回給Execute的調用者。

變量

操作中的管道可以初始化一個變量來捕獲結果。 初始化語法為:

$variable := pipeline

其中variable是變量的名稱。 聲明變量的操作不會產生輸出。

如果一個“range”操作初始化一個變量,則該變量將設置為叠代的連續元素。 另外,“範圍”可以聲明兩個變量,用逗號分隔。

range $index, $element := pipeline

在這種情況下,index和element分別被設置為數組/slice的索引和元素或map的鍵和元素的連續值。 請註意,如果只有一個變量,則會賦值元素; 這與Go range的約定沖突。

變量的範圍擴展到控制結構(“if”,“with”或“range”)的“end”操作,在沒有控制結構的情況下擴展到模板的末尾。 模板調用不會從其調用點繼承變量。

當執行開始時,$被設置為傳遞給Execute的數據參數,也就是點的起始值。

示例

下面是一些演示流水線和變量的單行模板示例。 所有輸出帶引號的單詞“output”:

{{"\"output\""}}
    一個字符串常量。
{{`"output"`}}
    一個原始字符串常量。
{{printf "%q" "output"}}
    一個函數調用。
{{"output" | printf "%q"}}
    參數來自前一個命令結果的函數調用。
{{printf "%q" (print "out" "put")}}
    一個帶括號的參數。
{{"put" | printf "%s%s" "out" | printf "%q"}}
    一個更展開的函數調用。
{{"output" | printf "%s" | printf "%q"}}
    一個長鏈條。
{{with "output"}}{{printf "%q" .}}{{end}}
    一個使用.的with操作。
{{with $x := "output" | printf "%q"}}{{$x}}{{end}}
    一個創建並使用變量的with操作。
{{with $x := "output"}}{{printf "%q" $x}}{{end}}
    一個在另一個操作中使用變量的with操作。
{{with $x := "output"}}{{$x | printf "%q"}}{{end}}
    相同,但使用管道。

函數

在執行期間,函數可以在兩個函數表中找到:首先在模板中,然後在全局函數表中找到。 默認情況下,模板中沒有定義函數,但可以使用Funcs方法添加它們。

預定義的全局函數名稱如下。

and
    返回參數的布爾AND,通過返回第一個空參數會最後一個參數,也就是
    "and x y" 表現為 "if x then y else x"。所有參數被計算。
    //非原文補充:如果熟悉javascript的&&操作符邏輯,就容易理解,這與其類同
call
    調用第一個參數,其必須為函數/方法,後續參數為目標方法的參數。
    因此 "call .X.Y 1 2" , 在Go語言中為 dot.X.Y(1, 2), Y是方法字段或map的key。
    第一個參數必須是產生函數類型值的評估結果(與預定義函數(如print)不同)。 
    該函數必須返回一個或兩個結果值,其中第二個結果值是類型錯誤。 
    如果參數與函數不匹配,或者返回的錯誤值非零,則停止執行。
html
    返回參數的文本表示的HTML轉義。該方法無法在html/template中使用, 存在錯誤。
index
    返回第一個參數的後續參數表示的索引的目標元素值。如"index x 1 2 3"在Go語言中為
    x[1][2][3]。被索引對象必須是map,slice,array。
js
    返回參數的文本表示的javascript轉義。
len
    返回參數的長度。
not
    返回單個參數的布爾NOT。
or
    返回參數的布爾OR,通過返回第一個非空元素,或最後一個元素。即
    "or x y"表現為"if x then x else y"。所有參數都被計算。
    //非原文補充:理解了javascript的||,則容易理解該邏輯。
print
    fmt.Sprint的別名
printf
    fmt.Sprintf的別名
println
    fmt.Sprintln的別名
urlquery
    返回參數的文本表示的URL查詢字符串轉義。
    該方法無法在html/template中使用, 存在錯誤。

布爾函數將任何零值設為false,並將非零值設為true。

還有一組定義為函數的二元比較運算符:

eq
    返回 arg1 == arg2 的布爾值
ne
    返回 arg1 != arg2 的布爾值
lt
    返回 arg1 < arg2 的布爾值
le
    返回 arg1 <= arg2 的布爾值
gt
    返回 arg1 > arg2 的布爾值
ge
    返回 arg1 >= arg2 的布爾值

對於更簡單的多路相等測試,eq(只)接受兩個或更多參數,並將第一個與第二個及後續參數相比,等效於:

arg1==arg2 || arg1==arg3 || arg1==arg4 ...

(與Go中的||不同,然而,eq是一個函數調用,所有參數都將被計算。)

比較函數僅適用於基本類型(或命名的基本類型,例如“type Celsius float32”)。 它們實現Go語言的比較規則,但忽略大小和確切類型,因此可以將任何有符號或無符號的整數值與任何其他整數值進行比較。 (算術值被比較,而不是位模式,因此所有負整數都小於所有無符號整數。)但是,像往常一樣,不能將int與float32等進行比較。

關聯的模板

每個模板都由創建時指定的字符串命名。 而且,每個模板都與零個或多個其他模板相關聯,可以按名稱調用它們; 這種關聯是可傳遞的,並形成模板的命名空間。

模板可以使用模板調用來實例化另一個相關聯的模板; 請參閱上面“模板”操作的解釋。 該名稱必須是與包含該調用的模板關聯的模板的名稱。

嵌套的模板定義

解析模板時,可能會定義另一個模板並將其與正在解析的模板關聯。 模板定義必須出現在模板的頂層,非常像Go程序中的全局變量。

這些定義的語法是用“定義”和“結束”操作包圍每個模板聲明。

定義操作通過提供字符串常量來命名正在創建的模板。 這是一個簡單的例子:

`{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}`

這定義了兩個模板T1和T2,以及第三個T3在執行時調用其他兩個模板。 最後它調用T3。 如果執行此模板將生成文本

ONE TWO

通過構建,模板可以僅存在於一個關聯中。 如果必須有可從多個關聯中尋址的模板,則必須多次解析模板定義以創建不同的* Template值,或者必須使用Clone或AddParseTree方法復制該模板定義。

可以多次調用解析來組裝各種關聯的模板; 請參閱ParseFiles和ParseGlob函數和方法,以獲取解析存儲在文件中的相關模板的簡單方法。

模板可以直接執行或通過ExecuteTemplate執行,後者執行由名稱標識的相關模板。 為了調用我們上面的例子,我們可以寫,

err := tmpl.Execute(os.Stdout, "no data needed")
if err != nil {
    log.Fatalf("execution failed: %s", err)
}

或通過名稱顯式調用特定的模板,

err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
if err != nil {
    log.Fatalf("execution failed: %s", err)
}

(譯)golang文本模板