Reputation: 11679
Aim: to insert a character every x characters in a string in Golang
Input: helloworldhelloworldhelloworld
Expected Output: hello-world-hello-world-hello-world
Attempts
Attempt one
package main
import (
"fmt"
"strings"
)
func main() {
s := "helloworldhelloworldhelloworld"
s = strings.Replace(s, "world", ",", -1)
fmt.Println(s)
}
results in: hello,hello,hello,
Attempt two
-
Attempt three
Problem
The reason that attempts two and three do not contain code snippets at the moment is that I am still thinking what approach should be used to insert a character every X characters in a string in Golang.
Upvotes: 6
Views: 13365
Reputation: 444
Pre-allocating memory and using copy
is faster than iteration over every single character.
https://go.dev/play/p/NqVYnOJGRLP
func insertNthCopy(in, sep string, n int) string {
count := len(in) / n // amount of lines to split
split := make([]byte, len(in)+len(sep)*count)
i, s := 0, 0
for ; i < count*n; i, s = i+n, s+n+len(sep) {
copy(split[s:], in[i:i+n])
copy(split[s+n:], sep)
}
copy(split[s:], in[i:]) // copy remainder if any
return string(split)
}
About six times faster than the accepted answer with provided sample string:
cpu: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
Benchmark_insertNthCopy
Benchmark_insertNthCopy-8 12404311 85.11 ns/op
Benchmark_insertNth
Benchmark_insertNth-8 2269458 524.5 ns/op
Upvotes: 0
Reputation: 17895
My take. I needed to add new lines (more than a single character) to long strings to wrap them.
func InsertNewLines(s string, n int) string {
var r = regexp.MustCompile("(.{" + strconv.Itoa(n) + "})")
return r.ReplaceAllString(s, "$1<wbr />")
}
Upvotes: 0
Reputation: 1
If you know your string is divisible by 5, then this could also be a solution. Definitely less efficient though than some of the other solutions posted.
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
fmt.Println(InsertEvery5("HelloWorld", "-"))
}
// Only works if len(str) mod 5 is 0
func InsertEvery5(str string, insert string) string {
re := regexp.MustCompile(`.{5}`) // Every 5 chars
parts := re.FindAllString(str, -1) // Split the string into 5 chars blocks.
return strings.Join(parts, insert) // Put the string back together
}
Upvotes: 0
Reputation: 76
I feel the following solution is worth mentioning:
package main
import "fmt"
var s = "helloworldhelloworldhelloworld"
func main() {
for i := 5; i < len(s); i += 6 {
s = s[:i] + "-" + s[i:]
}
fmt.Println(s)
}
https://play.golang.org/p/aMXOTgiNHf
Upvotes: 5
Reputation: 55483
My take:
import (
"fmt"
"regexp"
)
const s = "helloworldhelloworldhelloworld"
func Attempt1(s string) string {
var re = regexp.MustCompile(`(\Bhello|\Bworld)`)
return re.ReplaceAllString(s, "-$1")
}
func Attempt2(s string) string {
const chunkLen = len("hello")
out := make([]rune, len(s)+len(s)/chunkLen)
i, j := 1, 0
for _, c := range s {
out[j] = c
if i == len(s) {
break
}
j++
if i%chunkLen == 0 {
out[j] = '-'
j++
}
i++
}
return string(out)
}
func main() {
fmt.Println(Attempt1(s))
fmt.Println(Attempt2(s))
}
I should add that while it would be possible to implement
"approach 3" — that "split the source string in chunks of
five characters then join the chunks using the '-' character" one, —
it would still require scanning the source string rune-by-rune as my Attempt2()
does; so if you squint at it, you'll see that storing
a list of chunks and then joining them is doing more operations for
no real gain (and bigger memory footprint etc).
Upvotes: 1
Reputation: 241
According to the Go documentation strings are a read only Slice of bytes.. With that in mind there is an issue that comes up. What character set are you using? You can see some examples of where things get weird here and here.
Despite the complexity there still is a simple answer
s = strings.Replace(s, "hello", "hello-", -1)
s = strings.Replace(s, "world", "world-", -1)
Upvotes: 2
Reputation: 3200
https://play.golang.org/p/HEGbe7radf
This function just inserts '-' each Nth element
func insertNth(s string,n int) string {
var buffer bytes.Buffer
var n_1 = n - 1
var l_1 = len(s) - 1
for i,rune := range s {
buffer.WriteRune(rune)
if i % n == n_1 && i != l_1 {
buffer.WriteRune('-')
}
}
return buffer.String()
}
Upvotes: 10