英文:
text/template issue Parse() vs. ParseFiles()
问题
我正在尝试使用text/template包做一些简单的工作。我正在使用template顶部给出的示例。
我如何编写“parsed”文件,以便template.ParseFiles()
可以正确读取和执行它?
package main
import (
"text/template"
"os"
)
type Inventory struct {
Material string
Count uint
}
func main() {
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
// tmpl, err := template.New("test").ParseFiles("file.txt")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
}
文件.txt的内容:
{{.Count}} items are made of {{.Material}}
抛出的错误:
panic: template: test:1: "test" is an incomplete or empty template
goroutine 1 [running]:
main.main()
/tmp/templates/t.go:19 +0x21a
goroutine 2 [syscall]:
created by runtime.main
/var/tmp/portage/dev-lang/go-1.0.1/work/go/src/pkg/runtime/proc.c:221
我在golang playground上发布了这段代码的副本这里。
编辑#1:
我对这个问题进行了一些研究...由于实际上是Execute()
方法抛出异常,而不是ParseFiles()
部分,所以我检查了方法定义:
// Execute applies a parsed template to the specified data object,
// and writes the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
defer errRecover(&err)
value := reflect.ValueOf(data)
state := &state{
tmpl: t,
wr: wr,
line: 1,
vars: []variable{{"$", value}},
}
if t.Tree == nil || t.Root == nil {
state.errorf("%q is an incomplete or empty template", t.name)
}
state.walk(value, t.Root)
return
}
所以,凭直觉,我输出了内联的“非文件”样式的t.Tree的值,tmpl是:&parse.Tree{Name:"test", Root:(*parse.ListNode)(0xf840030700), funcs:[]map[string]interface {}(nil), lex:(*parse.lexer)(nil), token:[2]parse.item{parse.item{typ:6, val:""}, parse.item{typ:9, val:"{{"}}, peekCount:1, vars:[]string(nil)}
,当使用ParseFiles()
运行时,tmpl是:(*parse.Tree)(nil)
。我发现一个是解引用,一个是指针。这可能有助于解决这个谜题。
英文:
I'm trying to do some simple work with the text/template package. The sample given at the top of template is what I'm working with.
How do I write the 'parsed' file so template.ParseFiles()
properly reads and executes it?
package main
import (
"text/template"
"os"
)
type Inventory struct {
Material string
Count uint
}
func main() {
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
// tmpl, err := template.New("test").ParseFiles("file.txt")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }
}
/*
Contents of file.txt:
{{.Count}} items are made of {{.Material}}
Error thrown:
panic: template: test:1: "test" is an incomplete or empty template
goroutine 1 [running]:
main.main()
/tmp/templates/t.go:19 +0x21a
goroutine 2 [syscall]:
created by runtime.main
/var/tmp/portage/dev-lang/go-1.0.1/work/go/src/pkg/runtime/proc.c:221
*/
I have a copy of this code posted at the golang playground here
Edit #1:
I've been doing some research on this issue... since it's the Execute()
method that actually throws the exception, and not the ParseFiles()
part, I checked the method definition:
// Execute applies a parsed template to the specified data object,
// and writes the output to wr.
func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
defer errRecover(&err)
value := reflect.ValueOf(data)
state := &state{
tmpl: t,
wr: wr,
line: 1,
vars: []variable{{"$", value}},
}
if t.Tree == nil || t.Root == nil {
state.errorf("%q is an incomplete or empty template", t.name)
}
state.walk(value, t.Root)
return
}
So, on a hunch, I dumped the value of t.Tree for the inline 'non-file' style, tmpl is: &parse.Tree{Name:"test", Root:(*parse.ListNode)(0xf840030700), funcs:[]map[string]interface {}(nil), lex:(*parse.lexer)(nil), token:[2]parse.item{parse.item{typ:6, val:""}, parse.item{typ:9, val:"{{"}}, peekCount:1, vars:[]string(nil)}
and
when ran with ParseFiles()
, tmpl is: (*parse.Tree)(nil)
. I find it odd that one is a dereference, and one value is a pointer. This may help solve the riddle
答案1
得分: 17
sweaters := Inventory{"wool", 17}
tmpl, err := template.ParseFiles("file.txt")
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
panic(err)
}
如果你有很多文件,你可以使用ParseGlob:
tmpl, err := template.ParseGlob("*.txt")
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file2.txt", sweaters)
if err != nil {
panic(err)
}
英文:
sweaters := Inventory{"wool", 17}
tmpl, err := template.ParseFiles("file.txt")
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
panic(err)
}
If you have many files, you can use ParseGlob:
tmpl, err := template.ParseGlob("*.txt")
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file.txt", sweaters)
if err != nil {
panic(err)
}
err = tmpl.ExecuteTemplate(os.Stdout, "file2.txt", sweaters)
if err != nil {
panic(err)
}
答案2
得分: 11
在Go模板parseFiles中有一个小技巧。
只有与相同名称的模板才会被重用,否则会创建一个新的模板。
就像你的示例一样:
tmpl, err := template.New("test").ParseFiles("file.txt")
tmpl是名为"test"的模板,并关联另一个名为"file.txt"的模板,你在"test"模板上调用Execute时,这个模板是一个空模板,所以会引发错误"test is an incomplete or empty template"。
当你将模板名称更改为file.txt时,它就可以工作了。
tmpl, err := template.New("file.txt").ParseFiles("file.txt")
英文:
There is a little trick in Go template parseFiles.
func parseFiles(t *Template, filenames ...string) (*Template, error) {
if len(filenames) == 0 {
// Not really a problem, but be consistent.
return nil, fmt.Errorf("template: no files named in call to ParseFiles")
}
for _, filename := range filenames {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
s := string(b)
name := filepath.Base(filename)
// First template becomes return value if not already defined,
// and we use that one for subsequent New calls to associate
// all the templates together. Also, if this file has the same name
// as t, this file becomes the contents of t, so
// t, err := New(name).Funcs(xxx).ParseFiles(name)
// works. Otherwise we create a new template associated with t.
var tmpl *Template
if t == nil {
t = New(name)
}
if name == t.Name() {
tmpl = t
} else {
tmpl = t.New(name)
}
_, err = tmpl.Parse(s)
if err != nil {
return nil, err
}
}
return t, nil
}
Only the template with same name will be reuse, otherwise create new one.
as your sample:
tmpl, err := template.New("test").ParseFiles("file.txt")
tmpl is the template named "test", and associated another template named "file.txt", you call Execute on "test" template, this template is a empty template, so raise the error "test is an incomplete or empty template".
It worked when you change the template name to file.txt
tmpl, err := template.New("file.txt").ParseFiles("file.txt")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论