कैसे जाने के लिए कुशलता से तार में


727

गो में, stringयह एक आदिम प्रकार है, जिसका अर्थ है कि यह केवल-पढ़ने के लिए है, और इसके हर हेरफेर से एक नया तार पैदा होगा।

इसलिए यदि मैं परिणामी स्ट्रिंग की लंबाई को जाने बिना कई बार तार को जोड़ना चाहता हूं, तो इसके लिए सबसे अच्छा तरीका क्या है?

भोला तरीका होगा:

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

लेकिन यह बहुत कुशल नहीं लगता है।



1
नोट: यह प्रश्न और अधिकांश उत्तर append()भाषा में आने से पहले लिखे गए प्रतीत होते हैं, जो इसके लिए एक अच्छा समाधान है। यह तेजी से प्रदर्शन करेगा, copy()लेकिन अगर क्षमता पर्याप्त नहीं है, तो नए बैकिंग सरणी को आवंटित करने का मतलब है कि पहले स्लाइस बढ़ेगा। bytes.Bufferअभी भी समझ में आता है कि क्या आप इसकी अतिरिक्त सुविधा विधियाँ चाहते हैं या यदि पैकेज आप उपयोग कर रहे हैं तो यह अपेक्षा करता है।
थोमसट्रेटर

7
यह सिर्फ "बहुत अक्षम नहीं लगता"; यह एक विशिष्ट समस्या है कि हर नए गैर-सीएस भाड़े पर हमने कभी नौकरी पर पहले कुछ हफ्तों में रन बनाए हैं। यह द्विघात है - O (n * n)। संख्या अनुक्रम के बारे में सोचो 1 + 2 + 3 + 4 + ...:। यह n*(n+1)/2आधार के एक त्रिकोण का क्षेत्र है n। जब आप लूप में अपरिवर्तनीय तारों को जोड़ते हैं, तो आप आकार 1, फिर आकार 2, फिर आकार 3, आदि आवंटित करते हैं। यह द्विघात संसाधन उपभोग केवल इस से अधिक तरीकों से प्रकट होता है।
रोब

जवाबों:


856

नया रास्ता:

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
प्रिंट्लन के बजाय (स्ट्रिंग (बफर.बाइट्स ())); उपयोग सिर्फ प्रिंट्लन (बफर.ट्रिंग ()) कर सकता है
फिगमेंटबाइन

26
इसके बजाय buffer := bytes.NewBufferString(""), आप कर सकते हैं var buffer bytes.Buffer। आपको उन अर्धविरामों में से किसी की भी आवश्यकता नहीं है :)।
क्रेजी २be

66
अविश्वसनीय रूप से तेज। मेरे कार्यक्रम में कुछ भोले "+" स्ट्रिंग कॉनैट बने 3 मिनट से 1.3 सेकंड तक
मैलकम

10
"ओ (एन) समय" के लिए +1; मुझे लगता है कि इस तरह की और टिप्पणी करना महत्वपूर्ण है।
विरोधाभास

8
जाओ 1.10 कहते हैं strings.Builder , जो bytes.Buffer की तरह है, लेकिन तेजी से अपने अंतिम लक्ष्य एक स्ट्रिंग है जब।
जोश ब्लेकर स्नाइडर

272

स्ट्रिंग को समतल करने का सबसे कुशल तरीका बिलिन फ़ंक्शन का उपयोग करना है copy। मेरे परीक्षणों में, bytes.Bufferऑपरेटर के उपयोग की तुलना में यह दृष्टिकोण ~ 3x का उपयोग करने की तुलना में अधिक तेज और बहुत अधिक तेज (~ 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
बाइट्स। बफ़र मूल रूप से कॉपी के समान होना चाहिए (कुछ अतिरिक्त बहीखाता के साथ मुझे लगता है) और गति अलग नहीं है। तो मैं उस का उपयोग करेंगे :)। अंतर यह है कि बफर 0 बाइट्स से शुरू होता है, इसलिए इसे फिर से करना है (यह मुझे लगता है कि थोड़ा धीमा लगता है)। हालांकि उपयोग करने में आसान है।
अकटौ

5
buffer.Write(बाइट्स) की तुलना में 30% तेज है buffer.WriteString। [उपयोगी है यदि आप डेटा को प्राप्त कर सकते हैं []byte]
दानी-ब्र

34
ध्यान दें कि बेंचमार्क परिणाम विकृत हैं और प्रामाणिक नहीं हैं। विभिन्न मानदंड कार्यों को विभिन्न मूल्यों के साथ बुलाया जाएगाb.N , और इसलिए आप एक ही कार्य के निष्पादन समय की तुलना नहीं कर रहे हैं (जैसे एक फ़ंक्शन 1,000तार जोड़ सकता है , दूसरा एक एपेंड 10,000कर सकता है जो औसत में एक बड़ा अंतर ला सकता है। 1 परिशिष्ट का समय, BenchmarkConcat()उदाहरण के लिए)। आपको प्रत्येक मामले में (निश्चित रूप से नहीं b.N) एक ही परिशिष्ट गणना का उपयोग करना चाहिए , और (यानी, 2 छोरों को forलेकर) के शरीर के अंदर सभी संघनन करना चाहिए । b.Nfor
आईसीजे

18
इसके अतिरिक्त, प्रतिलिपि बेंचमार्क उस समय को स्पष्ट रूप से अनदेखा करके तिरछा हो जाता है जो आवंटन लेता है, जो अन्य बेंचमार्क में शामिल है।
gha.st

6
इसके अतिरिक्त, कॉपी बेंचमार्क परिणामी स्ट्रिंग की लंबाई जानने पर निर्भर करता है।
स्कारलेट

227

Go 1.10+ में strings.Builder, यहाँ है

लिखने के तरीकों का उपयोग करके एक स्ट्रिंग को कुशलतापूर्वक बनाने के लिए एक बिल्डर का उपयोग किया जाता है। यह मेमोरी कॉपी करने को कम करता है। शून्य मान उपयोग के लिए तैयार है।


उदाहरण

यह लगभग समान है 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 के तरीकों को मौजूदा इंटरफेस को ध्यान में रखते हुए लागू किया जा रहा है। ताकि आप अपने कोड में नए बिल्डर प्रकार पर आसानी से स्विच कर सकें।


बाइट्स से अंतर। बफ़र

  • यह केवल विकसित या रीसेट हो सकता है।

  • इसमें एक कॉपीचेक तंत्र अंतर्निहित है जो इसे गोपनीय रूप से कॉपी करने से रोकता है:

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

  • में bytes.Buffer, एक इस तरह अंतर्निहित बाइट्स पहुंच सकता है: (*Buffer).Bytes()

    • strings.Builder इस समस्या को रोकता है।
    • कभी-कभी, यह एक समस्या नहीं है, हालांकि और इसके बजाय वांछित।
    • उदाहरण के लिए: जब बाइट्स io.Readerआदि को पास कर दिया जाता है तो झांकने वाले व्यवहार के लिए ।

अधिक विवरण के लिए इसका स्रोत कोड देखें, यहां


5
'पलायन' से आपका क्या मतलब है? क्या आपका मतलब है कि स्ट्रिंग में भागना, या सिर्फ अंतर्निहित बाइट्स को उजागर किया जा सकता है?
माकड़ूमी

1
@ मखदुमी हां, 2, अंतर्निहित बाइट्स का जोखिम।
इनक गमूस

वर्थ नोटिंग strings.Builderने एक पॉइंटर रिसीवर का उपयोग करके अपने तरीकों को लागू किया, जिसने मुझे एक पल के लिए फेंक दिया। नतीजतन, मैं शायद एक का उपयोग करके बनाऊंगा new
डंकन जोन्स

@DuncanJones मैंने एक नोट जोड़ा है, क्योंकि यह ज्यादातर कैशिंग डेटा के लिए उपयोग किया जाता है, यह एक सूचक का उपयोग करने के लिए सामान्य है जब इसे फंकस आदि में साझा किया जाता है। उसी फंक में, आप इसे गैर-पॉइंटर के रूप में भी उपयोग कर सकते हैं।
इनक गमस

130

स्ट्रिंग्स पैकेज में एक लाइब्रेरी फ़ंक्शन है Join: http://golang.org/pkg/strings/#Join

Joinपरिशिष्ट समारोह में एक समान दृष्टिकोण शो कोड पर एक नजर 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

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
मेरा मानना ​​है कि ओपी रनटाइम जटिलता के बजाय मेमोरी जटिलता के बारे में अधिक चिंतित था, इस तथ्य को देखते हुए कि भोले स्ट्रिंग कॉन्फैक्शन का परिणाम हर बार नई मेमोरी आवंटन में होता है।
गालाकटर

15
इसकी धीमी गति अच्छी तरह से fmt.print के प्रयोग से संबंधित हो सकती हैbuffer.WriteString("\t"); buffer.WriteString(subs[i]);
रॉबर्ट जैक

मुझे पता है कि खुश हूँ कि की मेरी पसंदीदा तरीका (strings.Join)सबसे तेजी के रूप में रन से जबकि इस कह रही है कि (bytes.Buffer)विजेता है!
चेतबाहना

23

आप बाइट्स का एक बड़ा टुकड़ा बना सकते हैं और स्ट्रिंग स्लाइस का उपयोग करके इसमें छोटे तारों के बाइट्स को कॉपी कर सकते हैं। "प्रभावी गो" में दिया गया एक फ़ंक्शन है:

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 ( )इसे फिर से एक स्ट्रिंग में बदलने के लिए बाइट्स के बड़े स्लाइस पर उपयोग करें।


यह दिलचस्प है कि गो में ऐसा करने के कई तरीके हैं।
यित्जाक

11
प्रभावी रूप से, यह भी कहता है कि यह विचार इतना उपयोगी है कि इसे एक बिलिन में कैद किया गया था। तो आप अपने फ़ंक्शन को बदल सकते हैं append(slice, byte...), ऐसा लगता है।
अक्ताउ

23

यह सबसे तेज़ समाधान है जिसके लिए आपको पहले समग्र बफर आकार की जानकारी या गणना करने की आवश्यकता नहीं है:

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

मेरे बेंचमार्क के अनुसार , यह कॉपी सॉल्यूशन की तुलना में 20% धीमा है (6.72 के बजाय प्रति अपीयरेंस 8.1ns) लेकिन बाइट्स का उपयोग करने की तुलना में 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
ढेर अतिप्रवाह में आपका स्वागत है! सहायता केंद्र में संपादन सहायता के माध्यम से पढ़ने के लिए कुछ समय निकालें। स्टैक ओवरफ्लो पर प्रारूपण अन्य साइटों की तुलना में अलग है।
रिज़ियर १२

2
हालांकि यह कोड स्निपेट प्रश्न को हल कर सकता है, जिसमें स्पष्टीकरण सहित वास्तव में आपकी पोस्ट की गुणवत्ता में सुधार करने में मदद करता है। याद रखें कि आप भविष्य में पाठकों के लिए प्रश्न का उत्तर दे रहे हैं, और उन लोगों को आपके कोड सुझाव के कारणों का पता नहीं चल सकता है। कृपया अपने कोड को व्याख्यात्मक टिप्पणियों के साथ भीड़ न दें, इससे कोड और स्पष्टीकरण दोनों की पठनीयता कम हो जाती है!
रिज़ियर १२

सरल समाधान Fin
फिन 4

22

नोट 2018 में जोड़ा गया

1.10 गो से एक strings.Builderप्रकार है, अधिक विस्तार के लिए कृपया इस उत्तर पर एक नज़र डालें

201 से पहले के उत्तर

@ Cd1 और अन्य उत्तरों का बेंचमार्क कोड गलत है। b.Nबेंचमार्क फ़ंक्शन में सेट नहीं होना चाहिए। यह गो परीक्षण उपकरण द्वारा गतिशील रूप से निर्धारित करने के लिए निर्धारित किया जाता है कि परीक्षण का निष्पादन समय स्थिर है या नहीं।

एक बेंचमार्क फ़ंक्शन को एक ही परीक्षण 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 इंटेल कोर 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 सबसे तेज़ तरीका है; AppendPreAllocateनंबर 1 के काफी करीब है, लेकिन कोड लिखना आसान है।
  2. Concatगति और स्मृति उपयोग दोनों के लिए वास्तव में खराब प्रदर्शन है। इसका उपयोग न करें।
  3. Buffer#Writeऔर Buffer#WriteStringमूल रूप से गति में समान हैं, जो @ दानी-ब्र ने टिप्पणी में कहा है। विचार stringकरना वास्तव []byteमें गो में है, यह समझ में आता है।
  4. बाइट्स। बफ़र मूल रूप से एक ही समाधान का उपयोग Copyअतिरिक्त पुस्तक रखने और अन्य सामान के साथ करते हैं।
  5. Copyऔर Append64 के बूटस्ट्रैप आकार का उपयोग करें, बाइट्स के समान। बफ़र
  6. Appendअधिक मेमोरी और एलोकेशन का उपयोग करें, मुझे लगता है कि यह बढ़ते एल्गोरिथ्म से संबंधित है जो इसका उपयोग करता है। यह बाइट्स के रूप में तेजी से मेमोरी नहीं बढ़ा रहा है। बफ़र

सुझाव:

  1. साधारण कार्य के लिए जैसे कि ओपी क्या चाहता है, मैं उपयोग करूंगा Appendया AppendPreAllocate। यह काफी तेज और उपयोग में आसान है।
  2. यदि एक ही समय में बफर को पढ़ने और लिखने की आवश्यकता है, तो bytes.Bufferनिश्चित रूप से उपयोग करें । इसके लिए ही इसे बनाया गया है।

13

मेरा मूल सुझाव था

s12 := fmt.Sprint(s1,s2)

लेकिन उपर्युक्त उत्तर बाइट्स का उपयोग करके। बफ़र - WriteString () सबसे कुशल तरीका है।

मेरा प्रारंभिक सुझाव प्रतिबिंब और एक प्रकार स्विच का उपयोग करता है। देखें (p *pp) doPrintऔर(p *pp) printArg
बुनियादी प्रकारों के लिए कोई सार्वभौमिक स्ट्रिंगर () इंटरफ़ेस नहीं है, जैसा कि मैंने भोलेपन से सोचा था।

कम से कम, हालांकि, स्प्रिंट () आंतरिक रूप से एक बाइट्स का उपयोग करता है। बफ़र। इस प्रकार

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

स्मृति आवंटन के संदर्भ में स्वीकार्य है।

=> शीघ्र डिबग आउटपुट के लिए स्प्रिंट () संघनन का उपयोग किया जा सकता है।
=> अन्यथा बाइट्स का उपयोग करें। बफ़र ... WriteString


8
यह निर्मित नहीं है और यह कुशल नहीं है।
पेट्रोसो

पैकेज आयात करना (fmt की तरह) इसका मतलब यह बिल्ट नहीं है। यह मानक पुस्तकालय में है।
मैल्कम

यह धीमा है क्योंकि यह तर्कों पर प्रतिबिंब का उपयोग करता है। यह प्रभावोत्पादक है। अन्यथा यह
तार के

11

सीडी 1 के उत्तर पर विस्तार: आप कॉपी के बजाय () का उपयोग कर सकते हैं। परिशिष्ट () कभी-कभी बड़े अग्रिम प्रावधान करता है, जिसमें थोड़ी अधिक मेमोरी खर्च होती है, लेकिन समय की बचत होती है। मैंने आपके ऊपर दो और बेंचमार्क जोड़े । के साथ स्थानीय रूप से चलाएं

go test -bench=. -benchtime=100ms

मेरे थिंकपैड T400s पर इसकी पैदावार होती है:

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

4

यह @ cz1 (@ ) द्वारा दिए गए बेंचमार्क का वास्तविक संस्करण है 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

@PickBoy, कृपया अपनी बात को सही ठहराएं। आपको क्या लगता b.Nहै कि सार्वजनिक चर क्यों है ?
विटाली इसव

1
bN को बेंचमार्क फ़ंक्शन में सेट नहीं किया जाना चाहिए। यह गतिशील रूप से गो टेस्ट टूल द्वारा सेट किया गया है। एक बेंचमार्क फ़ंक्शन को एक ही टेस्ट bN बार चलाना चाहिए, लेकिन आपके कोड में (साथ ही @ cd1 का कोड), लूप में प्रत्येक परीक्षण एक अलग परीक्षा है (क्योंकि स्ट्रिंग की लंबाई बढ़ रही है)
PickBoy

@PickBoy, यदि आप b.Nगतिशील रूप से गो टेस्ट टूल सेट करते हैं, तो आप अलग-अलग लंबाई के स्ट्रिंग्स के साथ अलग-अलग टेस्ट-केस में वाइंड अप करेंगे। टिप्पणी
विटाली इसव

इसलिए आपको बीएन लूप के अंदर 10000 की तरह निश्चित पुनरावृत्तियों की एक आंतरिक लूप को जोड़ना चाहिए।
पिकबॉय

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
}

यह एक पाश के लिए पुनरावृत्तियों की एक श्रृंखला के माध्यम से एक स्ट्रिंग के निर्माण के ओपी के मुद्दे को संबोधित नहीं करता है।
कोडफ़ेस्टर

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
कृपया कोड केवल उत्तर पोस्ट न करें। कृपया बताएं कि यह कोड क्या करता है और इसका समाधान क्यों है।
कोरासेन

-1

स्मृति आवंटन आँकड़ों के साथ बेंचमार्क परिणाम। गितूब पर बेंचमार्क कोड की जाँच करें ।

प्रदर्शन का अनुकूलन करने के लिए स्ट्रिंग्स का उपयोग करें।

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)रूपांतरण के लिए डेटा की एक प्रतिलिपि बनाता है । पोस्ट किए गए अन्य समाधानों के साथ तुलना करते हुए, क्या आप अपने समाधान का एक भी नाम बता सकते हैं?
अंक

-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()केवल 2 पैरामीटर लेता है: एक टुकड़ा और एक विभाजक string
icza

इससे मदद नहीं मिल सकती
अंशु

यहां कुछ बदलाव जोड़ें।
अंशु
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.