Разделить строку по длине в Голанге
Кто-нибудь знает, как разделить строку в Голанге по длине?
Например, для разделения "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