2

我刚刚开始学习 Go,并制作了一个函数来解析带有标题的 markdown 文件,其中包含一些元数据(这些文件是博客文章)。

这是一个例子:

---
Some title goes here
19 September 2012
---
This is some content, read it.

我写了这个函数,它可以工作,但我觉得它很冗长和混乱,我看过各种字符串包,但我对 Go 的了解不够,最好的做法是知道我应该是什么做不同的事情,如果我能得到一些提示来清理它,我将不胜感激。(另外,我知道我不应该忽视这个错误)。

type Post struct {
    Title string
    Date string
    Body string
}
func loadPost(title string) *Post {
    filename := title + ".md"
    file, _ := ioutil.ReadFile("posts/" + filename)
    fileString := string(file)
    str := strings.Split(fileString, "---")
    meta := strings.Split(str[1], "\n")
    title = meta[1]
    date := meta[2]
    body := str[2]
    return &Post{Title: title, Date: date, Body: body}
}
4

3 回答 3

4

我觉得还不错。几个建议:

  • “posts/”中的硬编码斜线是平台相关的。您可以使用 path/filepath.Join 来避免这种情况。
  • 有 bytes.Split,所以你不需要字符串(文件)。
  • 您可以在不重复字段的情况下创建帖子:&Post{title, date, body}

或者,您可以找出正文从哪里开始,LastIndex(s, "--")并使用它来相应地索引文件内容。这样就避免了使用Split的分配。

const sep = "--"

func loadPost(content string) *Post {
    sepLength := len(sep)

    i := strings.LastIndex(content, sep)
    headers := content[sepLength:i]
    body := content[i+sepLength+1:]

    meta := strings.Split(headers, "\n")

    return &Post{meta[1], meta[2], body}
}
于 2012-09-19T07:58:37.110 回答
2

我同意这还不错。我将添加其他一些想法。

  • 正如 Thomas 所示,您不需要中间变量 title date 和 body。试一试,

    return &Post{
        Title: meta[1],
        Date: meta[2],
        Body: body,
    }
    

    确实可以省略字段名称,但我有时希望它们保持代码自记录。(我认为去兽医也喜欢他们。)

  • 我对字符串和字节切片大惊小怪,但可能比我应该做的要多。由于您一口气读取文件,因此您可能无需担心这一点。将所有内容转换为一个大字符串,然后将字符串切片是一种方便的处理方式,只要记住,如果您保留其中的任何部分,您就是将整个字符串固定在内存中。如果您的文件很大或者您有很多文件,并且您最终只保留了大多数文件的元数据,那么这可能不是要走的路。

  • 每个文件只有一个博客条目?如果是这样,我想我会提出托马斯建议的变体。验证第一个字节是 ---(或您的文件已损坏),然后使用 strings.Index(fileString[3:], "---")。当您有未知数量的段时,拆分更合适。在您的情况下,您只是在元数据之后寻找那个单一的分隔符。索引会在搜索元后找到它并完成,而不是搜索整个身体。(无论如何,如果正文包含字符串“---”怎么办?)

  • 最后,有些人会为此使用正则表达式。我还没有热身正则表达式,但无论如何,这是另一种方法。

于 2012-09-19T23:36:42.767 回答
1

索尼娅有一些很好的建议。以下是我在解析标头时可能遇到的问题。

http://play.golang.org/p/w-XYyhPj9n

package main

import (
    "fmt"
    "strings"
)

const sep = "---"

type parseError struct {
    msg string
}

func (e *parseError) Error() string {
    return e.msg
}

func parse(s string) (header []string, content string, err error) {
    if !strings.HasPrefix(s, sep) {
        return header, content, &parseError{"content does not start with `---`!"}
    }
    arr := strings.SplitN(s, sep, 3)
    if len(arr) < 3 {
        return header, content, &parseError{"header was not terminated with `---`!"}
    }
    header = strings.Split(strings.TrimSpace(arr[1]), "\n")
    content = strings.TrimSpace(arr[2])
    return header, content, nil
}

func main() {

    //
    f := `---
Some title goes here
19 September 2012
---
This is some content, read it. --Anonymous`

    header, content, err := parse(f)
    if err != nil {
        panic(err)
    }

    for i, val := range header {
        fmt.Println(i, val)
    }
    fmt.Println("---")
    fmt.Println(content)

    //
    f = `---
Some title goes here
19 September 2012
This is some content, read it.`

    _, _, err = parse(f)
    fmt.Println("Error:", err)

    //
    f = `
Some title goes here
19 September 2012
---
This is some content, read it.`

    _, _, err = parse(f)
    fmt.Println("Error:", err)
}
于 2012-09-19T23:53:45.877 回答