030
030

Reputation: 11679

How to insert a Character every X Characters in a String in Golang?

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

  1. Count number of characters
  2. For loop
  3. If X=5 then insert a -

Attempt three

  1. Scan combined with join

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

Answers (7)

debugger
debugger

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

472084
472084

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

Dylan
Dylan

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

J&#233;r&#244;me
J&#233;r&#244;me

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

kostix
kostix

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))
}

Playground link

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

Diablojoe
Diablojoe

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

Oleg
Oleg

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

Related Questions