Разделить строку по длине в Голанге

Кто-нибудь знает, как разделить строку в Голанге по длине?

Например, для разделения "helloworld" после каждых трех символов, поэтому он должен идеально возвращать массив "hel" "low" "orl" "d"?

В качестве альтернативы возможным решением будет также добавить новую строку после каждых трех символов.

Все идеи приветствуются!

Ответ 1

Обязательно конвертируйте вашу строку в кусочек руны: см.Строка фрагмента в буквы".

for автоматически преобразует string в rune, поэтому в этом случае не требуется никакого дополнительного кода для преобразования string в rune.

for i, r := range s {
    fmt.Printf("i%d r %c\n", i, r)
    // every 3 i, do something
}

r[n:n+3] будет лучше всего работать с кусочком руны.

Индекс будет увеличиваться на одну каждую руну, в то время как он может увеличиваться более чем на один для каждого байта в фрагменте строки: "世界": i будет 0 и 3: символ (руна) может состоять из нескольких байтов.


Например, рассмотрим s := "世a界世bcd界efg世": 12 рун. (см. play.golang.org)

Если вы попытаетесь разобрать его побайтово, вы пропустите (при наивном разбиении каждые 3-х символьную реализацию) некоторые "индексы по модулю 3" (равно 2, 5, 8 и 11), потому что индекс будет увеличиваться после этих значения:

for i, r := range s {
    res = res + string(r)
    fmt.Printf("i %d r %c\n", i, r)
    if i > 0 && (i+1)%3 == 0 {
        fmt.Printf("=>(%d) '%v'\n", i, res)
        res = ""
    }
}

Вывод:

i  0 r 世
i  3 r a   <== miss i==2
i  4 r 界
i  7 r 世  <== miss i==5
i 10 r b  <== miss i==8
i 11 r c  ===============> would print '世a界世bc', not exactly '3 chars'!
i 12 r d
i 13 r 界
i 16 r e  <== miss i==14
i 17 r f  ===============> would print 'd界ef'
i 18 r g
i 19 r 世 <== miss the rest of the string

Но если бы вы итерировали руны (a := []rune(s)), вы бы получили то, что ожидали, так как индекс увеличился бы по одной руне за раз, упрощая объединение ровно 3 символов:

for i, r := range a {
    res = res + string(r)
    fmt.Printf("i%d r %c\n", i, r)
    if i > 0 && (i+1)%3 == 0 {
        fmt.Printf("=>(%d) '%v'\n", i, res)
        res = ""
    }
}

Выход:

i 0 r 世
i 1 r a
i 2 r 界 ===============> would print '世a界'
i 3 r 世
i 4 r b
i 5 r c ===============> would print '世bc'
i 6 r d
i 7 r 界
i 8 r e ===============> would print 'd界e'
i 9 r f
i10 r g
i11 r 世 ===============> would print 'fg世'

Ответ 2

Также недавно была нужна функция для этого, см. пример использования здесь

func SplitSubN(s string, n int) []string {
    sub := ""
    subs := []string{}

    runes := bytes.Runes([]byte(s))
    l := len(runes)
    for i, r := range runes {
        sub = sub + string(r)
        if (i + 1) % n == 0 {
            subs = append(subs, sub)
            sub = ""
        } else if (i + 1) == l {
            subs = append(subs, sub)
        }
    }

    return subs
}

Ответ 3

Вот еще один пример (вы можете попробовать здесь):

package main

import (
    "fmt"
    "strings"
)

func ChunkString(s string, chunkSize int) []string {
    var chunks []string
    runes := []rune(s)

    if len(runes) == 0 {
        return []string{s}
    }

    for i := 0; i < len(runes); i += chunkSize {
        nn := i + chunkSize
        if nn > len(runes) {
            nn = len(runes)
        }
        chunks = append(chunks, string(runes[i:nn]))
    }
    return chunks
}

func main() {
    fmt.Println(ChunkString("helloworld", 3))
    fmt.Println(strings.Join(ChunkString("helloworld", 3), "\n"))
}

Ответ 4

Я попытался 3 версии для реализации функции, функция с именем "splitByWidthMake" является самой быстрой.

Эти функции игнорируют юникод, но только код ascii.

package main

import (
    "fmt"
    "strings"
    "time"
    "math"
)

func splitByWidthMake(str string, size int) []string {
    strLength := len(str)
    splitedLength := int(math.Ceil(float64(strLength) / float64(size)))
    splited := make([]string, splitedLength)
    var start, stop int
    for i := 0; i < splitedLength; i += 1 {
        start = i * size
        stop = start + size
        if stop > strLength {
            stop = strLength
        }
        splited[i] = str[start : stop]
    }
    return splited
}



func splitByWidth(str string, size int) []string {
    strLength := len(str)
    var splited []string
    var stop int
    for i := 0; i < strLength; i += size {
        stop = i + size
        if stop > strLength {
            stop = strLength
        }
        splited = append(splited, str[i:stop])
    }
    return splited
}

func splitRecursive(str string, size int) []string {
    if len(str) <= size {
        return []string{str}
    }
    return append([]string{string(str[0:size])}, splitRecursive(str[size:], size)...)
}

func main() {
    /*
    testStrings := []string{
        "hello world",
        "",
        "1",
    }
    */

    testStrings := make([]string, 10)
    for i := range testStrings {
        testStrings[i] = strings.Repeat("#", int(math.Pow(2, float64(i))))
    }

    //fmt.Println(testStrings)

    t1 := time.Now()
    for i := range testStrings {
        _ = splitByWidthMake(testStrings[i], 2)
        //fmt.Println(t)
    }
    elapsed := time.Since(t1)
    fmt.Println("for loop version elapsed: ", elapsed)


    t1 = time.Now()
    for i := range testStrings {
        _ = splitByWidth(testStrings[i], 2)
    }
    elapsed = time.Since(t1)
    fmt.Println("for loop without make version elapsed: ", elapsed)




    t1 = time.Now()
    for i := range testStrings {
        _ = splitRecursive(testStrings[i], 2)
    }
    elapsed = time.Since(t1)
    fmt.Println("recursive version elapsed: ", elapsed)

}

Ответ 5

Простое решение с использованием regex

re: = regexp.MustCompile((\S{3})) x: = re.FindAllStringSubmatch( "HelloWorld", -1) fmt.Println(х)

https://play.golang.org/p/mfmaQlSRkHe