【问题标题】:How to truncate a string in a Golang template如何截断 Golang 模板中的字符串
【发布时间】:2014-05-05 06:50:05
【问题描述】:

在 golang 中,有没有办法截断 html 模板中的文本?

例如,我的模板中有以下内容:

{{ range .SomeContent }}
 ....
    {{ .Content }}
 ....

{{ end }

{{ .Content }} 制作:Interdum et malesuada 在 faucibus 中成名。 Aliquam tempus sem ipsum, vel accumsan felis vulputate id。 Donec ultricies sem purus,非 aliquam orci dignissim 等。整数简历 mi arcu。 Pellentesque a ipsum quis velit venenatis vulputate vulputate ut enim。

我想减少到 25 个字符。

【问题讨论】:

标签: go


【解决方案1】:

您可以在模板中使用printf,它充当fmt.Sprintf。在您的情况下,截断字符串就像:

"{{ printf \"%.25s\" .Content }}"

【讨论】:

  • 那不是%<width>.<precision>f 它是如何处理字符串的?以及我应该怎么做才能修剪最后一个字符,因为 .%.-1s 不起作用
  • 修剪最后1个字符由%-1s完成
【解决方案2】:

您可以使用documentation 中的slice。以下示例必须有效:

{{ slice .Content  0 25}}

slice 返回将其第一个参数分割为剩余参数的结果。因此,“切片 x 1 2”在 Go 语法中是 x[1:2], 而“slice x”是x[:],“slice x 1”是x[1:],“slice x 1 2 3”是x[1:2:3]。第一个参数必须是字符串、切片或数组。

注意它不能处理索引超出范围的问题和多字节字符串——以防.Content 是一个字符串。

【讨论】:

  • 非常简单明了。我会确保检查长度,以免超出范围。
【解决方案3】:

更新:现在下面的代码是 unicode 兼容的,适用于那些使用国际程序的人。

需要注意的是,下面的 bytes.Runes("string") 是一个 O(N) 操作,就像从 runes 到字符串的转换一样,所以这段代码在字符串上循环了两次。为 PreviewContent() 执行下面的代码可能会更有效

func (c ContentHolder) PreviewContent() string {
    var numRunes = 0
    for index, _ := range c.Content {
         numRunes++
         if numRunes > 25 {
              return c.Content[:index]
         }
    }
    return c.Content
}

对于此功能的用途,您有几个选择。假设您有某种类型的内容持有者,可以使用以下内容:

type ContentHolder struct {
    Content string
    //other fields here
}

func (c ContentHolder) PreviewContent() string {
    // This cast is O(N)
    runes := bytes.Runes([]byte(c.Content))
    if len(runes) > 25 {
         return string(runes[:25])
    }
    return string(runes)
}

那么您的模板将如下所示:

{{ range .SomeContent }}
....
{{ .PreviewContent }}
....
{{ end }}

另一个选项是创建一个函数,该函数将获取字符串的前 25 个字符。代码如下所示(@Martin DrLík 对代码的修订,link to code

package main
import (
    "html/template"
    "log"
    "os"
)

func main() {

    funcMap := template.FuncMap{

        // Now unicode compliant
        "truncate": func(s string) string {
             var numRunes = 0
             for index, _ := range s {
                 numRunes++
                 if numRunes > 25 {
                      return s[:index]
                 }
            }
            return s
       },
    }

    const templateText = `
    Start of text
    {{ range .}}
    Entry: {{.}}
    Truncated entry: {{truncate .}}
    {{end}}
    End of Text
    `
    infoForTemplate := []string{
        "Stackoverflow is incredibly awesome",
        "Lorem ipsum dolor imet",
        "Some more example text to prove a point about truncation",
        "ПриветМирПриветМирПриветМирПриветМирПриветМирПриветМир",
    }

    tmpl, err := template.New("").Funcs(funcMap).Parse(templateText)
    if err != nil {
        log.Fatalf("parsing: %s", err)
    }

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

}

这个输出:

Start of text

Entry: Stackoverflow is incredibly awesome
Truncated entry: Stackoverflow is incredib

Entry: Lorem ipsum dolor imet
Truncated entry: Lorem ipsum dolor imet

Entry: Some more example text to prove a point about truncation
Truncated entry: Some more example text to

Entry: ПриветМирПриветМирПриветМирПриветМирПриветМирПриветМир
Truncated entry: ПриветМирПриветМирПриветМ

End of Text

【讨论】:

  • unicode 呢?它不适用于 unicode。比如这个……
  • 使用[]rune(c.Content) 而不是bytes.Runes([]byte(c.Content))
【解决方案4】:

Unicode 字符串需要更多魔法

这是不正确的,见下文

import "unicode/utf8"

func Short( s string, i int) string {
    if len( s ) < i {
        return s
    }
    if utf8.ValidString( s[:i] ) {
        return s[:i]
    }
    // The omission.
    // In reality, a rune can have 1-4 bytes width (not 1 or 2)
    return s[:i+1] // or i-1
}

但是上面的i不是字符数。它是字节数。在play.golang.org上链接到此代码

我希望这会有所帮助。


编辑

更新:检查字符串长度。请参阅下面的@geoff 评论

查看that 的答案,然后玩here。这是另一种解决方案。

package main

import "fmt"

func Short( s string, i int ) string {
    runes := []rune( s )
    if len( runes ) > i {
        return string( runes[:i] )
    }
    return s
}

func main() {
    fmt.Println( Short( "Hello World", 5 ) )
    fmt.Println( Short( "Привет Мир", 5 ) )
}

但如果您对字节长度感兴趣:

func truncateStrings(s string, n int) string {
    if len(s) <= n {
        return s
    }
    for !utf8.ValidString(s[:n]) {
        n--
    }
    return s[:n]
}

play.golang.org。这个函数永远不会恐慌(如果 n >= 0),但你可以获得一个空字符串play.golang.org


另外,请记住这个实验包golang.org/x/exp/utf8string

utf8string 包提供了一种通过符文而不是字节来索引字符串的有效方法。

【讨论】:

  • 请注意,在上一个示例中,如果您尝试将字符串截断到比字符串长的长度,您可能会感到恐慌。我修改了你的 Short 方法来修复它:func Short( s string, i int ) string { var runes = []rune( s ) if len(runes) &gt; i { return string( runes[:i] ) } return s }
【解决方案5】:

有很多很好的答案,但有时不切字截断更方便用户。 Hugo 为此提供了template function。 但是在 Hugo 之外很难使用,所以我实现了它:

func TruncateByWords(s string, maxWords int) string {
    processedWords := 0
    wordStarted := false
    for i := 0; i < len(s); {
        r, width := utf8.DecodeRuneInString(s[i:])
        if !unicode.IsSpace(r) {
            i += width
            wordStarted = true
            continue
        }

        if !wordStarted {
            i += width
            continue
        }

        wordStarted = false
        processedWords++
        if processedWords == maxWords {
            const ending = "..."
            if (i + len(ending)) >= len(s) {
                // Source string ending is shorter than "..."
                return s
            }

            return s[:i] + ending
        }

        i += width
    }

    // Source string contains less words count than maxWords.
    return s
}
    

这里是这个功能的测试:

func TestTruncateByWords(t *testing.T) {
    cases := []struct {
        in, out string
        n       int
    }{
        {"a bcde", "a...", 1},
        {"a b", "a b", 2},
        {"a b", "a b", 3},

        {"a b c", "a b c", 2},
        {"a b cd", "a b cd", 2},
        {"a b cde", "a b...", 2},

        {"  a   b    ", "  a   b...", 2},

        {"AB09C_D EFGH", "AB09C_D...", 1},
        {"Привет Гоферам", "Привет...", 1},
        {"Here are unicode spaces", "Here are...", 2},
    }

    for i, c := range cases {
        got := TruncateByWords(c.in, c.n)
        if got != c.out {
            t.Fatalf("#%d: %q != %q", i, got, c.out)
        }
    }
}

【讨论】:

    【解决方案6】:
    str := "xxxx"
    n := 2
    if len(str) > n {
        fmt.Println(str[:n])
    }
    

    免得说我们需要四分之一的 ascii 字符串

    str[:len(str)/4]
    

    【讨论】:

      猜你喜欢
      • 2011-04-24
      • 2013-02-07
      • 2013-07-09
      • 2012-12-04
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多