ChatGPT解决这个技术问题 Extra ChatGPT

如何在go中有效地连接字符串

在 Go 中,string 是一种原始类型,这意味着它是只读的,并且对它的每次操作都会创建一个新字符串。

因此,如果我想在不知道结果字符串长度的情况下多次连接字符串,那么最好的方法是什么?

天真的方法是:

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

但这似乎不是很有效。

还有一个bench
注意:这个问题和大多数答案似乎是在 append() 进入该语言之前编写的,这是一个很好的解决方案。它会像 copy() 一样快速执行,但会首先增长切片,即使这意味着如果容量不足则分配一个新的支持数组。如果您想要它的额外便利方法或者您使用的包需要它,bytes.Buffer 仍然有意义。
它不仅“看起来非常低效”;它有一个特定的问题,我们在工作的最初几周内遇到的每一个新的非 CS 员工都会遇到。它是二次的 - O(n*n)。想想数字序列:1 + 2 + 3 + 4 + ...。它是 n*(n+1)/2,底边为 n 的三角形的面积。当您在循环中附加不可变字符串时,您分配大小 1,然后是大小 2,然后是大小 3,等等。这种二次方的资源消耗表现在更多方面。

I
Inanc Gumus

新的方法:

从 Go 1.10 开始,有一个 strings.Builder 类型,please take a look at this answer for more detail

旧方式:

使用 bytes 包。它有一个实现 io.WriterBuffer 类型。

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) 时间内完成的。


而不是 println(string(buffer.Bytes()));使用可以只做 println(buffer.String())
您可以使用 var buffer bytes.Buffer 代替 buffer := bytes.NewBufferString("")。您也不需要任何这些分号:)。
难以置信的快。在我的程序中将一些天真的“+”字符串连接从 3 分钟缩短到 1.3 秒。
+1 表示“O(n) 时间”;我认为发表更多这样的言论很重要。
Go 1.10 添加了 strings.Builder,它类似于 bytes.Buffer,但当您的最终目标是字符串时更快。
I
Inanc Gumus

在 Go 1.10+ 中有 strings.Builderhere

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 sb strings.Builder

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

    fmt.Println(sb.String())
}

Click to see this on the playground

支持的接口

StringBuilder 的方法是在考虑现有接口的情况下实现的。这样您就可以在代码中轻松切换到新的 Builder 类型。

增长(int)-> bytes.Buffer#Grow

Len() int -> bytes.Buffer#Len

Reset() -> bytes.Buffer#Reset

String() 字符串 -> fmt.Stringer

Write([]byte) (int, error) -> io.Writer

WriteByte(byte) 错误 -> io.ByteWriter

WriteRune(rune) (int, error) -> bufio.Writer#WriteRune - bytes.Buffer#WriteRune

WriteString(string) (int, error) -> io.stringWriter

与 bytes.Buffer 的区别

它只能增长或重置。

它有一个内置的 copyCheck 机制,可以防止意外复制它: func (b *Builder) copyCheck() { ... }

在 bytes.Buffer 中,可以像这样访问底层字节:(*Buffer).Bytes()。 strings.Builder 可以防止这个问题。有时,这不是问题,而是需要的。例如:对于将字节传递给 io.Reader 等时的偷看行为。

strings.Builder 可以防止这个问题。

有时,这不是问题,而是需要的。

例如:对于将字节传递给 io.Reader 等时的偷看行为。

bytes.Buffer.Reset() 倒带并重用底层缓冲区,而 strings.Builder.Reset() 不会,它会分离缓冲区。

笔记

不要复制 StringBuilder 值,因为它会缓存基础数据。

如果要共享 StringBuilder 值,请使用指向它的指针。

查看其源代码了解更多详情,here


你说的“逃避”是什么意思?你的意思是字符串中的转义,或者只是可以暴露底层字节?
@makhdumi 是的,第二,底层字节的暴露。
值得注意的是 strings.Builder 使用指针接收器来实现它的方法,这让我很震惊。因此,我可能会使用 new 创建一个。
@DuncanJones 但是,我添加了一条注释,因为它主要用于缓存数据,因此在跨函数等共享它时使用指向它的指针是正常的。在同一个函数中,您也可以将它用作非指针。
另一个可能很重要的区别:strings.Builder.Reset() 将底层切片设置为 nil(无内存重用)。其中 bytes.Buffer.Reset()[]bytes 设置为零长度,保持分配的基础数组。当在 sync.Pool 中重用 strings.Builder 时,这让我有点吃惊,这似乎完全没用。
I
Inanc Gumus

如果您知道要预分配的字符串的总长度,那么连接字符串的最有效方法可能是使用内置函数 copy。如果您事先不知道总长度,请不要使用 copy,而是阅读其他答案。

在我的测试中,这种方法比使用 bytes.Buffer 快约 3 倍,比使用运算符 + 快得多(约 12,000 倍)。此外,它使用更少的内存。

我创建了 a test case 来证明这一点,结果如下:

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

bytes.Buffer 应该与副本基本相同(我猜还有一些额外的簿记),并且速度没有那么不同。所以我会用那个:)。不同之处在于缓冲区以 0 字节开始,因此它必须重新分配(我猜这让它看起来有点慢)。不过更容易使用。
buffer.Write(字节)比 buffer.WriteString 快 30%。 [如果您可以将数据作为 []byte 获取,则很有用]
请注意,基准测试结果失真且不真实。将使用不同的 b.N 值调用不同的基准函数,因此您不会比较要执行的同一任务的执行时间(例如,一个函数可能附加 1,000 字符串,另一个可能附加 10,000这会对 1 个追加的平均时间产生很大的影响,例如在 BenchmarkConcat() 中)。您应该在每种情况下使用相同的附加计数(当然不是 b.N),并在 for 的主体内进行所有连接,范围为 b.N(即嵌入 2 个 for 循环)。
此外,复制基准通过显式忽略分配所花费的时间而产生偏差,这包含在其他基准中。
此外,复制基准依赖于知道结果字符串的长度。
I
Inanc Gumus

如果您有一个想要有效地转换为字符串的字符串切片,那么您可以使用这种方法。否则,请查看其他答案。

字符串包中有一个名为 Join 的库函数:http://golang.org/pkg/strings/#Join

查看 Join 的代码显示了与 Append 函数相似的方法 Kinopiko 写道: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

当您必须遍历不是 [] 字符串的内容时不起作用。
P
Pika Supports Ukraine

我刚刚在我自己的代码(递归树遍历)中对上面发布的最佳答案进行了基准测试,简单的 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,"")
}

我相信 OP 更关心内存复杂性而不是运行时复杂性,因为天真的字符串连接每次都会导致新的内存分配。
这可能与使用 fmt.Fprint 而不是 buffer.WriteString("\t"); buffer.WriteString(subs[i]); 有关
我很高兴知道我首选的 (strings.Join) 方法运行得最快,而从 this(bytes.Buffer) 是赢家!
s
slfan
package main

import (
  "fmt"
)

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

欢迎来到堆栈溢出!花点时间阅读帮助中心的 editing help。 Stack Overflow 上的格式设置与其他站点不同。
虽然此代码段可能会解决问题,但 including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性注释来挤满你的代码,这会降低代码和解释的可读性!
这根本不能回答这个问题。 fmt.Sprintf 是连接简单字符串时效率最差的方法。根据这个 benchfmt.Sprintf 甚至比添加运算符 (+) OP 提到的效率更低。
佚名

您可以创建一大片字节并使用字符串切片将短字符串的字节复制到其中。 “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 中有很多方法可以做到这一点。
在有效的 go 中,它还说这个想法非常有用,它被捕获在一个内置函数中。所以你可以用 append(slice, byte...) 替换你的函数,看起来。
r
rog

这是不需要您首先知道或计算整体缓冲区大小的最快解决方案:

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

通过我的 benchmark,它比复制解决方案慢 20%(每次追加 8.1ns 而不是 6.72ns),但仍比使用 bytes.Buffer 快 55%。


I
Inanc Gumus

2018 年添加的注释

从 Go 1.10 开始,有一个 strings.Builder 类型,please take a look at this answer for more detail

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

结论:

CopyPreAllocate 是最快的方法; AppendPreAllocate 非常接近 No.1,但更容易编写代码。 Concat 在速度和内存使用方面的表现都非常差。不要使用它。 Buffer#Write 和 Buffer#WriteString 在速度上基本相同,这与@Dani-Br 在评论中所说的相反。考虑到字符串在 Go 中确实是 []byte,这是有道理的。 bytes.Buffer 基本上使用与 Copy 相同的解决方案,并带有额外的簿记和其他内容。 Copy 和 Append 使用 64 的引导大小,与 bytes 相同。Buffer Append 使用更多的内存和分配,我认为这与它使用的增长算法有关。它的内存增长速度不如 bytes.Buffer

建议:

对于诸如 OP 想要的简单任务,我会使用 Append 或 AppendPreAllocate。它足够快且易于使用。如果需要同时读取和写入缓冲区,当然使用 bytes.Buffer 。这就是它的设计目的。


P
Peter Buchmann

我原来的建议是

s12 := fmt.Sprint(s1,s2)

但是上面使用 bytes.Buffer - WriteString() 的答案是最有效的方法。

我最初的建议使用反射和类型切换。 See (p *pp) doPrint and (p *pp) printArg
正如我天真地认为的那样,基本类型没有通用的 Stringer() 接口。

至少,Sprint() 在内部使用了 bytes.Buffer。因此

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

在内存分配方面是可以接受的。

=> Sprint() 连接可用于快速调试输出。 => 否则使用 bytes.Buffer ... WriteString


它不是内置的,效率不高。
导入一个包(如 fmt)意味着它不是内置的。它在标准库中。
它之所以慢只是因为它对它的论点进行了反思。这是有效的。否则,它的效率不亚于使用 strings.Join 连接
P
Peter Buchmann

扩展 cd1 的答案:您可以使用 append() 而不是 copy()。 append() 进行了更大的预先准备,花费了更多的内存,但节省了时间。我在您的顶部添加了 two more benchmarks。在本地运行

go test -bench=. -benchtime=100ms

在我的 thinkpad T400s 上,它产生:

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

V
Vitaly Isaev

这是@cd1 (Go 1.8, linux x86_64) 提供的基准测试的实际版本,修复了@icza 和@PickBoy 提到的错误。

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,请证明您的观点。为什么您认为 b.N 是公共变量?
bN 不应该在基准函数中设置。它是由 go 测试工具动态设置的。基准函数应该运行相同的测试 bN 次,但是在您的代码(以及 @cd1 的代码)中,循环中的每个测试都是不同的测试(因为字符串的长度正在增长)
@PickBoy,如果您让 go 测试工具集 b.N 动态,您将在不同的测试用例中得到不同长度的字符串。请参阅comment
这就是为什么您应该在 bN 循环内添加一个固定迭代次数(例如 10000 次)的内部循环。
X
Xian Shu

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

K
Krish Bhanushali

我使用以下方法:-

package main

import (
    "fmt"
    "strings"
)

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

这并没有解决 OP 通过一系列迭代和 for 循环构建字符串的问题。
r
rajni kant
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))
}

请不要发布仅代码的答案。请解释这段代码的作用以及为什么它是解决方案。
S
Sumer

简单易消化的解决方案。评论中的详细信息。复制覆盖切片的元素。我们正在切片单个元素并覆盖它。

package main

import (
    "fmt"
)

var N int = 100000

func main() {
    slice1 := make([]rune, N, N)
    //Efficient with fast performance, Need pre-allocated memory
    //We can add a check if we reached the limit then increase capacity
    //using append, but would be fined for data copying to new array. Also append happens after the length of current slice.
    for i := 0; i < N; i++ {
        copy(slice1[i:i+1], []rune{'N'})
    }
    fmt.Println(slice1)

    //Simple but fast solution, Every time the slice capacity is reached we get a fine of effort that goes
    //in copying data to new array
    slice2 := []rune{}
    for i := 0; i <= N; i++ {
        slice2 = append(slice2, 'N')
    }
    fmt.Println(slice2)

}

h
hechen0

带有内存分配统计信息的基准测试结果。检查 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。
u
user2288856
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))

这是一个非常慢的解决方案,因为它使用反射,它解析格式字符串,并为 []byte(s1) 转换制作数据副本。将其与发布的其他解决方案进行比较,您能说出您的解决方案的一个优点吗?
A
Anshu

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

此代码甚至无法编译:strings.Join() 仅采用 2 个参数:切片和分隔符 string
这无济于事
在此处添加一些更改。