goで文字列を効率的に連結する方法


727

Goでは、a stringはプリミティブ型です。つまり、読み取り専用であり、操作するたびに新しい文字列が作成されます。

したがって、結果の文字列の長さがわからないまま文字列を何度も連結したい場合、それを行うための最良の方法は何ですか?

素朴な方法は次のとおりです。

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

しかし、それは非常に効率的ではないようです。


7
もう1つのベンチ
Ivan Black

1
注:この質問とほとんどの回答はappend()、言語に入る前に書かれたようです。これは、これに対する良い解決策です。のように高速に実行されますがcopy()、容量が不足している場合に新しいバッキングアレイを割り当てることになっても、スライスが最初に大きくなります。 bytes.Buffer追加の便利なメソッドが必要な場合や、使用しているパッケージがそれを期待している場合でも、意味があります。
thomasrutter 2017

7
「非常に非効率的に見える」だけではありません。それは私たちが今までに得たすべての新しい非CS採用が仕事の最初の数週間で遭遇するという特定の問題があります。それは二次式です-O(n * n)。数列について考えてみましょう1 + 2 + 3 + 4 + ...。それn*(n+1)/2は、底辺の三角形の面積ですn。ループに不変文字列を追加するときは、サイズ1、次にサイズ2、次にサイズ3を割り当てます。この二次的なリソース消費は、これ以上の方法で現れます。
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()))の代わりに; println(buffer.String())を使用するだけでよい
FigmentEngine

26
代わりにbuffer := bytes.NewBufferString("")、あなたがすることができますvar buffer bytes.Buffer。また、これらのセミコロンは必要ありません:)。
crazy2be

66
信じられないほど速い。私のプログラムの単純な「+」文字列連結を3分から1.3 秒に変更しました
マルコム

10
「O(n)時間」の+1。このようにもっと発言することが重要だと思います。
2014年

8
Go 1.10はstrings.Builderを追加します。これはbytes.Bufferに似ていますが、最終目標が文字列の場合は高速です。
Josh Bleecher Snyder 2017

272

文字列を連結する最も効率的な方法は、組み込み関数を使用することcopyです。私のテストでは、このアプローチは、使用するよりも約3倍速くbytes.Buffer、演算子を使用するよりもはるかに高速です(約12,000倍)+。また、メモリ使用量も少なくなります。

これを証明するテストケースを作成ました。結果は次のとおりです。

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 2014年

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 2016年

227

Go 1.10以降ではstrings.Builderここにあります

Builderは、Writeメソッドを使用して文字列を効率的に構築するために使用されます。メモリのコピーを最小限に抑えます。ゼロ値はすぐに使用できます。


とほぼ同じ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のメソッドは、既存のインターフェースを考慮して実装されています。コードで新しいビルダータイプに簡単に切り替えることができます。


bytes.Bufferとの違い

  • 成長またはリセットのみ可能です。

  • 誤ってコピーするのを防ぐcopyCheckメカニズムが組み込まれています。

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

  • ではbytes.Buffer、次のように基になるバイトにアクセスできます(*Buffer).Bytes()

    • strings.Builder この問題を防ぎます。
    • 時には、これは問題ではありませんが、代わりに望まれます。
    • 例:バイトがio.Readeretcなどに渡されるときのピーク動作の場合。

詳細については、こちらのソースコードをご覧ください


5
「エスケープ」とはどういう意味ですか?文字列のエスケープを意味するのか、それとも基礎となるバイトを公開できるのか?
makhdumi 2018年

1
@makhdumiはい、2番目に、基礎となるバイトの露出。
Inanc Gumus 2018

注目に値するのはstrings.Builder、ポインターレシーバーを使用してそのメソッドを実装することです。その結果、私はおそらくを使用して1つを作成しnewます。
Duncan Jones、

@DuncanJonesただし、メモを追加しました。これは主にデータのキャッシュに使用されるため、func間で共有する場合はポインタへのポインタを使用するのが通常です。同じfuncでは、ポインタ以外のポインタとしても使用できます。
Inanc Gumus

130

文字列パッケージには次のライブラリ関数がありますJoinhttp : //golang.org/pkg/strings/#Join

のコードを見ると、JoinKinopikoが書いた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
[]文字列ではないものをループする必要がある場合は機能しません。
マルコム

42

私は上記の自分のコード(再帰的なツリーウォーク)で投稿された上位の回答をベンチマークしました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 2012

15
この遅い速度は、代わりにfmt.Fprintを使用することに関連している可能性があります buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will

これが勝者であると言っている(strings.Join)間、私は最速として私の好ましい実行方法を知ってうれしいです!(bytes.Buffer)
チェタバハナ2015年

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 2014年

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
Stack Overflowへようこそ!ヘルプセンターの編集ヘルプを一読してください。Stack Overflowでのフォーマットは他のサイトとは異なります。
Rizier123 2016年

2
このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。また、説明コメントでコードを混雑させないようにしてください。これにより、コードと説明の両方が読みにくくなります。
Rizier123 2016年

シンプルなソリューション👍–
Finn

22

2018年に追加されたメモ

Go 1.10以降、strings.Builderタイプがあります詳しくは、この回答をご覧ください

201xより前の回答

@ cd1のベンチマークコードとその他の回答が間違っています。b.Nベンチマーク機能で設定することは想定されていません。go testツールによって動的に設定され、テストの実行時間が安定しているかどうかを判断します。

ベンチマーク関数は同じテスト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.2 GHz 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基本的にはダニ-Brはコメントで言っ@に反して、速度が同じです。string確か[]byteに囲碁で考えると、それは理にかなっています。
  4. bytes.Bufferは基本的にCopy、追加の簿記やその他のものと同じソリューションを使用します。
  5. CopyそしてAppend、bytes.Bufferと同じ64のブートストラップ・サイズを使用
  6. Appendより多くのメモリと割り当てを使用します。これは、使用する成長アルゴリズムに関連していると思います。それはバイトほど速くメモリを成長させていません。

提案:

  1. OPが望むものなどの単純なタスクの場合、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 2013

(fmtのような)パッケージのインポートは、組み込みではないことを意味します。標準ライブラリにあります。
マルコム

それはそれがその引数に反映を使用しているという理由だけで遅いです。それは効率的です。それ以外の場合は、
文字列で

11

cd1の答えを拡張すると、copy()の代わりにappend()を使用できます。append()は、より大きな事前プロビジョニングを行い、少し多くのメモリを消費しますが、時間を節約します。私はあなたの上部にさらに2つのベンチマークを追加しました。ローカルで実行

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によって言及されたバグの修正を含むGo 1.8、@ cd1(、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 2017

1
bNはベンチマーク関数で設定することを想定していません。goテストツールによって動的に設定されます。ベンチマーク関数は同じテストをbN回実行する必要がありますが、コード(および@ cd1のコード)では、ループ内のすべてのテストは異なるテストです(文字列の長さが長くなるため)
PickBoy

@ PickBoy、goテストツールをb.N動的に設定すると、さまざまなテストケースでさまざまな長さの文字列が表示されます。コメントを
Vitaly Isaev 2017

そのため、bNループ内に、10000回のように、反復回数が固定された内部ループを追加する必要があります。
PickBoy 2017

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() 「strings」パッケージから

型の不一致がある場合(intとstringを結合しようとしている場合など)は、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()スライスとセパレーターの2つのパラメーターのみを受け取りますstring
icza

これは仕方がない
アンシュ

ここに変更を追加します。
安州
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.