효율적으로 문자열을 연결하는 방법


727

Go에서 a string는 기본 유형이므로 읽기 전용이므로 모든 조작시 새 문자열이 작성됩니다.

결과 문자열의 길이를 모른 채 문자열을 여러 번 연결하려면 가장 좋은 방법은 무엇입니까?

순진한 방법은 다음과 같습니다.

s := ""
for i := 0; i < 1000; i++ {
    s += getShortStringFromSomewhere()
}
return s

그러나 그것은 매우 효율적으로 보이지 않습니다.



1
참고 :이 질문과 대부분의 답변은 append()언어에 오기 전에 작성된 것으로 보이며 이는 좋은 해결책입니다. 성능은 빠르지 copy()만 용량이 충분하지 않은 경우 새 백업 배열을 할당하는 것을 의미하더라도 슬라이스가 먼저 커집니다. bytes.Buffer추가 편리한 방법을 원하거나 사용중인 패키지가 원하는 경우에도 여전히 의미가 있습니다.
thomasrutter

7
단지 "매우 비효율적 인 것"이 아닙니다. 우리가 입수 한 모든 비 CS 직원은 처음 몇 주 동안 직무를 수행해야한다는 특정한 문제가 있습니다. 이차-O (n * n)입니다. 숫자 순서를 생각하십시오 1 + 2 + 3 + 4 + .... 그것은이다 n*(n+1)/2,베이스의 삼각형의 면적 n. 루프에 불변 문자열을 추가 할 때 크기 1, 크기 2, 크기 3 등을 할당합니다. 이 2 차 자원 소비는 이보다 더 많은 방법으로 나타납니다.
Rob

답변:


856

새로운 길:

Go 1.10부터 strings.Builder유형 이 있으므로 자세한 내용은이 답변을 참조하십시오 .

올드 웨이 :

bytes패키지를 사용하십시오 . Buffer구현 하는 유형이 있습니다 io.Writer.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}

이것은 O (n) 시간 안에 이루어집니다.


24
println (string (buffer.Bytes ())) 대신에; 사용은 (buffer.String ())에 println 할 수
FigmentEngine

26
대신 buffer := bytes.NewBufferString("")할 수 있습니다 var buffer bytes.Buffer. 세미콜론도 필요하지 않습니다 :).
crazy2be

66
엄청나게 빠릅니다. 내 프로그램에서 순진한 "+"문자열 연결을 3 분에서 1.3 초로 만들었 습니다 .
Malcolm

10
"O (n) 시간"동안 +1; 이런 말을 더하는 것이 중요하다고 생각합니다.
모순

8
Go 1.10 은 bytes.Buffer와 같은 strings.Builder를 추가 하지만 최종 목표가 문자열 인 경우 더 빠릅니다.
Josh Bleecher Snyder

272

문자열을 연결하는 가장 효율적인 방법은 내장 함수를 사용하는 것 copy입니다. 내 테스트에서 그 접근 방식은 bytes.Buffer연산자를 사용하는 것보다 ~ 3 배 빠르며 연산자를 사용하는 것보다 훨씬 빠릅니다 (~ 12,000x) +. 또한 적은 메모리를 사용합니다.

이를 증명하기 위해 테스트 사례 를 만들었 으며 결과는 다음과 같습니다.

BenchmarkConcat  1000000    64497 ns/op   502018 B/op   0 allocs/op
BenchmarkBuffer  100000000  15.5  ns/op   2 B/op        0 allocs/op
BenchmarkCopy    500000000  5.39  ns/op   0 B/op        0 allocs/op

테스트 코드는 다음과 같습니다.

package main

import (
    "bytes"
    "strings"
    "testing"
)

func BenchmarkConcat(b *testing.B) {
    var str string
    for n := 0; n < b.N; n++ {
        str += "x"
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); str != s {
        b.Errorf("unexpected result; got=%s, want=%s", str, s)
    }
}

func BenchmarkBuffer(b *testing.B) {
    var buffer bytes.Buffer
    for n := 0; n < b.N; n++ {
        buffer.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); buffer.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", buffer.String(), s)
    }
}

func BenchmarkCopy(b *testing.B) {
    bs := make([]byte, b.N)
    bl := 0

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        bl += copy(bs[bl:], "x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); string(bs) != s {
        b.Errorf("unexpected result; got=%s, want=%s", string(bs), s)
    }
}

// Go 1.10
func BenchmarkStringBuilder(b *testing.B) {
    var strBuilder strings.Builder

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        strBuilder.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); strBuilder.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", strBuilder.String(), s)
    }
}

6
bytes.Buffer는 기본적으로 사본과 동일하게 수행해야하며 (일부 추가 부기 포함) 속도는 다르지 않습니다. 그래서 나는 그것을 사용할 것입니다 :). 차이점은 버퍼가 0 바이트로 시작하여 재 할당해야한다는 것입니다 (이것이 조금 느리게 보입니다). 그래도 사용하기 쉽습니다.
Aktau

5
buffer.Write(바이트)가보다 30 % 빠릅니다 buffer.WriteString. [데이터를 얻을 수 있다면 유용 []byte]
Dani-Br

34
벤치 마크 결과가 왜곡되고 확실하지 않습니다. 다른 벤치 마크 함수는 다른 값으로 호출 b.N되므로 동일한 작업의 수행 시간을 비교하지 않습니다 (예 : 하나의 함수는 1,000문자열을 추가 하고 다른 하나는 추가 10,000하여 평균에 큰 차이를 만들 수 있음) BenchmarkConcat()예를 들어 1의 시간이 추가 됩니다. 각 경우에 동일한 추가 횟수를 사용해야하며 (확실히 아님 b.N) for범위 의 본문 내에서 모든 연결을 수행해야합니다 b.N(즉, 2 개의 for루프가 포함됨).
icza

18
또한 복사 벤치 마크는 다른 벤치 마크에 포함 된 할당 시간을 명시 적으로 무시하여 왜곡됩니다.
gha.st

6
또한 복사 벤치 마크는 결과 문자열의 길이를 알고 있어야합니다.
Skarllot

227

Go 1.10+에는 strings.Builder, here이 있습니다 .

Builder는 Write 메서드를 사용하여 문자열을 효율적으로 작성하는 데 사용됩니다. 메모리 복사를 최소화합니다. 0 값을 사용할 준비가되었습니다.


와 거의 동일합니다 bytes.Buffer.

package main

import (
    "strings"
    "fmt"
)

func main() {
    // ZERO-VALUE:
    //
    // It's ready to use from the get-go.
    // You don't need to initialize it.
    var str strings.Builder

    for i := 0; i < 1000; i++ {
        str.WriteString("a")
    }

    fmt.Println(str.String())
}

놀이터에서 이것을 보려면 클릭하십시오 .


노트

  • 기본 데이터를 캐시하므로 StringBuilder 값을 복사하지 마십시오.
  • StringBuilder 값을 공유하려면 포인터를 사용하십시오.

지원되는 인터페이스

StringBuilder의 메소드는 기존 인터페이스를 염두에두고 구현되고 있습니다. 코드에서 쉽게 새 빌더 유형으로 전환 할 수 있습니다.


바이트와의 차이점

  • 커지거나 재설정 만 할 수 있습니다.

  • 여기에는 실수로 복사하는 것을 방지하는 copyCheck 메커니즘이 내장되어 있습니다.

    func (b *Builder) copyCheck() { ... }

  • 에서 다음 bytes.Buffer과 같이 기본 바이트에 액세스 할 수 있습니다 (*Buffer).Bytes().

    • strings.Builder 이 문제를 방지합니다.
    • 때때로, 이것은 문제가되지 않고 대신에 바람직합니다.
    • 예를 들면 : 바이트 등을 전달할 때의 엿보기 동작의 경우 io.Reader.

자세한 내용은 소스 코드를 확인하십시오 ( 여기) .


5
'탈출'이란 무슨 뜻입니까? 문자열에서 이스케이프하거나 기본 바이트가 노출 될 수 있다는 의미입니까?
makhdumi

1
@makhdumi 네, 기본 바이트의 노출입니다.
Inanc Gumus

주목할 가치가 strings.Builder있는 것은 포인터 수신기를 사용하여 메소드를 구현하는 것입니다. 결과적으로 아마도을 사용하여 만들 것입니다 new.
던컨 존스

@DuncanJones 그러나 데이터를 캐싱하는 데 주로 사용되므로 메모를 추가했습니다. 펑크 등을 통해 공유 할 때 포인터를 사용하는 것이 일반적입니다. 같은 펑크에서는 비 포인터로도 사용할 수 있습니다.
Inanc Gumus

130

문자열 패키지에는 다음과 같은 라이브러리 함수가 있습니다 Join. http://golang.org/pkg/strings/#Join

코드를 Join살펴보면 Kinopiko가 작성한 Append 함수에 대한 비슷한 접근 방식이 표시됩니다. https://golang.org/src/strings/strings.go#L420

용법:

import (
    "fmt";
    "strings";
)

func main() {
    s := []string{"this", "is", "a", "joined", "string\n"};
    fmt.Printf(strings.Join(s, " "));
}

$ ./test.bin
this is a joined string

21
[] 문자열이 아닌 무언가를 반복해야하는 경우에는 작동하지 않습니다.
Malcolm

42

방금 내 코드 (재귀 트리 워크)에서 위에 게시 된 최상위 답변을 벤치마킹했으며 간단한 concat 연산자가 실제로보다 빠릅니다 BufferString.

func (r *record) String() string {
    buffer := bytes.NewBufferString("");
    fmt.Fprint(buffer,"(",r.name,"[")
    for i := 0; i < len(r.subs); i++ {
        fmt.Fprint(buffer,"\t",r.subs[i])
    }
    fmt.Fprint(buffer,"]",r.size,")\n")
    return buffer.String()
}

다음 코드는 0.81 초가 걸렸습니다.

func (r *record) String() string {
    s := "(\"" + r.name + "\" ["
    for i := 0; i < len(r.subs); i++ {
        s += r.subs[i].String()
    }
    s += "] " + strconv.FormatInt(r.size,10) + ")\n"
    return s
} 

0.61 초 밖에 걸리지 않았습니다. 이것은 아마도 새로운 생성의 오버 헤드 때문일 것입니다 BufferString.

업데이트 : 또한 join기능을 벤치마킹 했으며 0.54 초 만에 실행되었습니다.

func (r *record) String() string {
    var parts []string
    parts = append(parts, "(\"", r.name, "\" [" )
    for i := 0; i < len(r.subs); i++ {
        parts = append(parts, r.subs[i].String())
    }
    parts = append(parts, strconv.FormatInt(r.size,10), ")\n")
    return strings.Join(parts,"")
}

5
순진한 문자열 연결로 인해 매번 새로운 메모리 할당이 발생한다는 사실을 고려할 때 OP는 런타임 복잡성보다는 메모리 복잡성에 더 관심이 있다고 생각합니다.
galaktor

15
느린 속도는 fmt.Fprint 대신 fmt.Fprint를 사용하는 것과 관련이있을 수 있습니다. buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will

말을 (strings.Join)하면서 내가 가장 선호하는 달리기 방법 (bytes.Buffer) 승자 라는 것을 알게되어 기쁩니다 !
Chetabahana

23

큰 바이트 조각을 만들고 문자열 조각을 사용하여 짧은 문자열의 바이트를 복사 할 수 있습니다. "Effective Go"에는 다음과 같은 기능이 있습니다.

func Append(slice, data[]byte) []byte {
    l := len(slice);
    if l + len(data) > cap(slice) { // reallocate
        // Allocate double what's needed, for future growth.
        newSlice := make([]byte, (l+len(data))*2);
        // Copy data (could use bytes.Copy()).
        for i, c := range slice {
            newSlice[i] = c
        }
        slice = newSlice;
    }
    slice = slice[0:l+len(data)];
    for i, c := range data {
        slice[l+i] = c
    }
    return slice;
}

그런 다음 작업이 완료되면 string ( )큰 바이트 조각을 사용 하여 문자열로 다시 변환하십시오.


Go에서 이것을 수행하는 방법이 너무 많다는 것은 흥미 롭습니다.
Yitzhak

11
또한 효과적인 아이디어는 아이디어가 너무 유용하여 내장 된 아이디어로 포착되었다고 말합니다. 따라서 함수를로 바꿀 수 있습니다 append(slice, byte...).
Aktau

23

전체 버퍼 크기를 먼저 알거나 계산할 필요가없는 가장 빠른 솔루션입니다.

var data []byte
for i := 0; i < 1000; i++ {
    data = append(data, getShortStringFromSomewhere()...)
}
return string(data)

벤치 마크 에 따르면 복사 솔루션보다 20 % 느리지 만 (6.72ns가 아닌 추가 당 8.1ns) bytes.Buffer를 사용하는 것보다 여전히 55 % 빠릅니다.


23
package main

import (
  "fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    out := fmt.Sprintf("%s %s ",str1, str2)
    fmt.Println(out)
}

2
스택 오버플로에 오신 것을 환영합니다! 잠시 동안 도움말 센터에서 수정 도움말 을 읽어보십시오 . 스택 오버플로에서의 서식은 다른 사이트와 다릅니다.
Rizier123

2
이 코드 스 니펫은 문제를 해결할 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 앞으로 독자들에게 질문에 대한 답변을 제공하므로 해당 사람들이 코드 제안의 이유를 모를 수도 있습니다. 설명 주석으로 코드를 복잡하게 만들지 마십시오. 이렇게하면 코드와 설명의 가독성이 떨어집니다!
Rizier123

간단한 해결책 👍
Finn

22

2018 년에 추가 된 메모

Go 1.10부터 strings.Builder유형 이 있으므로 자세한 내용은이 답변을 참조하십시오 .

201x 이전 답변

@ cd1의 벤치 마크 코드 및 기타 답변이 잘못되었습니다. b.N벤치 마크 기능으로 설정되어 있지 않아야합니다. 테스트 실행 시간이 안정적인지 확인하기 위해 go 테스트 도구에 의해 동적으로 설정됩니다.

벤치 마크 함수는 동일한 테스트 b.N시간을 실행 해야하며 루프 내부의 테스트는 각 반복마다 동일해야합니다. 내부 루프를 추가하여 수정합니다. 또한 다른 솔루션에 대한 벤치 마크를 추가합니다.

package main

import (
    "bytes"
    "strings"
    "testing"
)

const (
    sss = "xfoasneobfasieongasbg"
    cnt = 10000
)

var (
    bbb      = []byte(sss)
    expected = strings.Repeat(sss, cnt)
)

func BenchmarkCopyPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        bs := make([]byte, cnt*len(sss))
        bl := 0
        for i := 0; i < cnt; i++ {
            bl += copy(bs[bl:], sss)
        }
        result = string(bs)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppendPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, cnt*len(sss))
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        buf := bytes.NewBuffer(make([]byte, 0, cnt*len(sss)))
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkCopy(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
        for i := 0; i < cnt; i++ {
            off := len(data)
            if off+len(sss) > cap(data) {
                temp := make([]byte, 2*cap(data)+len(sss))
                copy(temp, data)
                data = temp
            }
            data = data[0 : off+len(sss)]
            copy(data[off:], sss)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppend(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64)
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWrite(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.Write(bbb)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWriteString(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkConcat(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < cnt; i++ {
            str += sss
        }
        result = str
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

환경은 OS X 10.11.6, 2.2GHz Intel Core i7입니다.

시험 결과:

BenchmarkCopyPreAllocate-8         20000             84208 ns/op          425984 B/op          2 allocs/op
BenchmarkAppendPreAllocate-8       10000            102859 ns/op          425984 B/op          2 allocs/op
BenchmarkBufferPreAllocate-8       10000            166407 ns/op          426096 B/op          3 allocs/op
BenchmarkCopy-8                    10000            160923 ns/op          933152 B/op         13 allocs/op
BenchmarkAppend-8                  10000            175508 ns/op         1332096 B/op         24 allocs/op
BenchmarkBufferWrite-8             10000            239886 ns/op          933266 B/op         14 allocs/op
BenchmarkBufferWriteString-8       10000            236432 ns/op          933266 B/op         14 allocs/op
BenchmarkConcat-8                     10         105603419 ns/op        1086685168 B/op    10000 allocs/op

결론:

  1. CopyPreAllocate 가장 빠른 방법입니다. AppendPreAllocateNo.1과 거의 비슷하지만 코드를 작성하는 것이 더 쉽습니다.
  2. Concat속도와 메모리 사용량 모두에서 성능이 실제로 떨어집니다. 사용하지 마십시오.
  3. Buffer#Write그리고 Buffer#WriteString기본적으로 다니 - 브롬은 코멘트에서 밝혔다 @ 것과는 달리, 속도가 동일합니다. 고려는 string참으로 []byte그 의미가, 이동에.
  4. bytes.Buffer는 기본적으로 Copy여분의 책 보관 및 기타 자료와 동일한 솔루션을 사용합니다 .
  5. CopyAppend , bytes.Buffer와 동일 (64)의 부트 스트랩 크기를 사용
  6. Append더 많은 메모리와 할당량을 사용하면 사용하는 증가 알고리즘과 관련이 있다고 생각합니다. 바이트만큼 빠른 메모리를 늘리지 않습니다.

암시:

  1. 이러한 영업 이익이 원하는 것을 같은 간단한 작업을 위해, 내가 사용하는 것 Append또는AppendPreAllocate 합니다. 충분히 빠르고 사용하기 쉽습니다.
  2. 버퍼를 동시에 읽고 쓸 필요가 있다면 bytes.Buffer물론 사용하십시오 . 그것이 설계된 것입니다.

13

내 원래 제안은

s12 := fmt.Sprint(s1,s2)

그러나 bytes.Buffer를 사용하는 위의 답변 -WriteString () 이 가장 효율적인 방법입니다.

나의 초기 제안은 리플렉션과 타입 스위치를 사용합니다. 참조 (p *pp) doPrint(p *pp) printArg
내가 순진하게 생각했던대로 보편적 인 스트링거 () 인터페이스는 기본 유형이 없습니다.

적어도 Sprint ()는 내부적 으로 bytes.Buffer를 사용합니다. 그러므로

`s12 := fmt.Sprint(s1,s2,s3,s4,...,s1000)`

메모리 할당 측면에서 허용됩니다.

=> Sprint () 연결은 빠른 디버그 출력에 사용될 수 있습니다.
=> 그렇지 않으면 bytes.Buffer ... WriteString을 사용하십시오.


8
내장되어 있지 않으며 비효율적입니다.
peterSO

fmt와 같은 패키지를 가져 오면 기본적으로 제공되지 않습니다. 표준 라이브러리에 있습니다.
Malcolm

인수에 대한 반영을 사용하기 때문에 속도가 느립니다. 효율적입니다. 그렇지 않으면
문자열로

11

cd1의 답변으로 확장 : copy () 대신 append ()를 사용할 수 있습니다. append ()는 더 큰 사전 프로비저닝을 제공하여 메모리 비용이 조금 들지만 시간은 절약됩니다. 나는 당신의 상단에 두 개의 벤치 마크 추가했습니다 . 로 로컬로 실행

go test -bench=. -benchtime=100ms

내 Thinkpad T400에서 다음을 얻습니다.

BenchmarkAppendEmpty    50000000         5.0 ns/op
BenchmarkAppendPrealloc 50000000         3.5 ns/op
BenchmarkCopy           20000000        10.2 ns/op

4

이것은 @icza 및 @PickBoy가 언급 한 버그를 수정 하여 @ cd1 ( Go 1.8, linux x86_64)에서 제공하는 벤치 마크의 실제 버전입니다 .

Bytes.Buffer아니라 7시간이 빨리를 통해 직접 문자열 연결에 비해 +연산자.

package performance_test

import (
    "bytes"
    "fmt"
    "testing"
)

const (
    concatSteps = 100
)

func BenchmarkConcat(b *testing.B) {
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < concatSteps; i++ {
            str += "x"
        }
    }
}

func BenchmarkBuffer(b *testing.B) {
    for n := 0; n < b.N; n++ {
        var buffer bytes.Buffer
        for i := 0; i < concatSteps; i++ {
            buffer.WriteString("x")
        }
    }
}

타이밍 :

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op

수동으로 bN을 설정하는 것이 패키지 테스트의 벤치 마크 기능을 사용하는 올바른 방법이라고 생각하지 않습니다.
PickBoy

@PickBoy, 당신의 관점을 정당화하십시오. 왜 b.N공공 변수 라고 생각 하십니까?
Vitaly Isaev

1
bN은 벤치 마크 기능에서 설정되지 않아야합니다. go 테스트 도구에 의해 동적으로 설정됩니다. 벤치 마크 함수는 동일한 테스트 bN 시간을 실행해야하지만 코드 (@ cd1 코드)에서 루프의 모든 테스트는 다른 테스트입니다 (문자열 길이가 길어지기 때문에)
PickBoy

@PickBoy, 이동 테스트 도구 세트를 b.N동적으로 설정 하면 다른 테스트 사례에서 다른 길이의 문자열로 마무리됩니다. 댓글
Vitaly Isaev

그렇기 때문에 bN 루프 안에 10000과 같이 고정 된 반복 횟수의 내부 루프를 추가해야합니다.
PickBoy

3

goutils.JoinBetween

 func JoinBetween(in []string, separator string, startIndex, endIndex int) string {
    if in == nil {
        return ""
    }

    noOfItems := endIndex - startIndex

    if noOfItems <= 0 {
        return EMPTY
    }

    var builder strings.Builder

    for i := startIndex; i < endIndex; i++ {
        if i > startIndex {
            builder.WriteString(separator)
        }
        builder.WriteString(in[i])
    }
    return builder.String()
}

1

나는 다음을 사용하여 그것을한다 :-

package main

import (
    "fmt"
    "strings"
)

func main (){
    concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator. 
    fmt.Println(concatenation) //abc
}

이것은 for 루프와 함께 일련의 반복을 통해 문자열을 작성하는 OP의 문제를 다루지 않습니다.
codeforester

1
package main

import (
"fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    result := make([]byte, 0)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)

    fmt.Println(string(result))
}

3
코드 만 답변을 게시하지 마십시오. 이 코드의 기능과 왜 솔루션인지 설명하십시오.
Korashen

-1

메모리 할당 통계와 벤치 마크 결과. github 에서 벤치 마크 코드 확인 .

strings.Builder를 사용하여 성능을 최적화하십시오.

go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/hechen0/goexp/exps
BenchmarkConcat-8                1000000             60213 ns/op          503992 B/op          1 allocs/op
BenchmarkBuffer-8               100000000               11.3 ns/op             2 B/op          0 allocs/op
BenchmarkCopy-8                 300000000                4.76 ns/op            0 B/op          0 allocs/op
BenchmarkStringBuilder-8        1000000000               4.14 ns/op            6 B/op          0 allocs/op
PASS
ok      github.com/hechen0/goexp/exps   70.071s

여기에서 작성하는 원래 테스트 사례에 대해 @ cd1에 크레딧을 제공하십시오.
colm.anseo

-2
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))

5
리플렉션을 사용하고 형식 문자열을 구문 분석하고 []byte(s1)변환 을 위해 데이터 사본을 작성하므로 매우 느린 솔루션 입니다. 게시 된 다른 솔루션과 비교하여 솔루션의 단일 이점을 말할 수 있습니까?
pts

-5

strings.Join() "문자열"패키지에서

형식이 일치하지 않으면 (int 및 문자열에 가입하려는 경우와 같이) RANDOMTYPE (변경하려는 작업)

전의:

package main

import (
    "fmt"
    "strings"
)

var intEX = 0
var stringEX = "hello all you "
var stringEX2 = "people in here"


func main() {
    s := []string{stringEX, stringEX2}
    fmt.Println(strings.Join(s, ""))
}

출력 :

hello all you people in here

4
이 코드는 컴파일조차하지 않습니다 strings.Join(): 슬라이스와 분리기의 두 매개 변수 만 취합니다 string.
icza

도움이 될 수 없습니다
Anshu

여기에 몇 가지 변경 사항을 추가하십시오.
Anshu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.