Подтвердить что ты не робот

Как усечь строку в шаблоне Golang

В golang есть способ обрезать текст в html-шаблоне?

Например, в моем шаблоне есть следующее:

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

{{ end }

{{ .Content }} производит: Interdum et malesuada fames ac ante ipsum primis в faucibus. Aliquam tempus sem ipsum, vel accumsan felis vulputate id. Donec ultricies sem purus, non aliquam orci dignissim et. Целая биография mi arcu. Pellentesque a ipsum quis velit venenatis - вульгатная вульгатная утилита.

Я хотел бы уменьшить это до 25 символов.

4b9b3361

Ответ 1

Вы можете использовать printf в шаблонах, который действует как fmt.Sprintf. В вашем случае обрезка строки будет такой же простой, как:

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

Ответ 2

Обновление: Теперь код ниже соответствует юникоду для тех, кто работает с международными программами.

Следует отметить, что bytes.Runes( "string" ) ниже - это операция O (N), как и преобразование из рун в строку, поэтому этот код дважды перебирает строку. Скорее всего, будет более эффективным сделать код ниже для 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, ссылка на код)

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

Ответ 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 ниже

См. который отвечает, и воспроизведите здесь. Это другое решение.

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 обеспечивает эффективный способ индексирования строк по rune, а не по байтам.

Ответ 5

Есть много хороших ответов, но иногда это более удобно для усечения без сокращения слов. Hugo предлагает функцию шаблона для него. Но это трудно использовать за пределами 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 !isSeparator(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)
        }
    }
}