गो में io.Reader से स्ट्रिंग तक


129

मेरे पास एक io.ReadCloserवस्तु है (एक http.Responseवस्तु से)।

संपूर्ण स्ट्रीम को stringऑब्जेक्ट में बदलने का सबसे कुशल तरीका क्या है ?

जवाबों:


175

संपादित करें:

1.10 के बाद से, तार। उदाहरण:

buf := new(strings.Builder)
n, err := io.Copy(buf, r)
// check errors
fmt.Println(buf.String())

संबंधित जानकारी कम है

संक्षिप्त उत्तर यह है कि यह कुशल नहीं होगा क्योंकि एक स्ट्रिंग में परिवर्तित करने के लिए बाइट सरणी की एक पूरी प्रतिलिपि करने की आवश्यकता होती है। यहाँ उचित (गैर-कुशल) तरीका है जो आप चाहते हैं:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
s := buf.String() // Does a complete copy of the bytes in the buffer.

यह प्रतिलिपि एक सुरक्षा तंत्र के रूप में की जाती है। तार अपरिवर्तनीय हैं। यदि आप [[] बाइट को स्ट्रिंग में बदल सकते हैं, तो आप स्ट्रिंग की सामग्री को बदल सकते हैं। हालाँकि, जाने से आप असुरक्षित पैकेज का उपयोग कर सुरक्षा तंत्र को निष्क्रिय कर सकते हैं। असुरक्षित पैकेज का उपयोग अपने जोखिम पर करें। उम्मीद है कि नाम ही एक अच्छी चेतावनी है। यहां बताया गया है कि मैं इसे असुरक्षित उपयोग कैसे करूंगा:

buf := new(bytes.Buffer)
buf.ReadFrom(yourReader)
b := buf.Bytes()
s := *(*string)(unsafe.Pointer(&b))

वहां हम जाते हैं, आपने अब अपने बाइट सरणी को कुशलता से स्ट्रिंग में बदल दिया है। वास्तव में, यह सब इसे टाइप करने के लिए स्ट्रिंग को टाइप करने की प्रणाली को चालित करता है। इस विधि के लिए कुछ युगल हैं:

  1. इस बात की कोई गारंटी नहीं है कि यह सभी गो कंपाइलरों में काम करेगा। हालांकि यह योजना -9 gc संकलक के साथ काम करता है, यह आधिकारिक कार्यान्वयन में उल्लिखित "कार्यान्वयन विवरण" पर निर्भर करता है। आप यह भी गारंटी नहीं दे सकते कि यह सभी आर्किटेक्चर पर काम करेगा या जीसी में नहीं बदला जाएगा। दूसरे शब्दों में, यह एक बुरा विचार है।
  2. वह तार परस्पर है! यदि आप उस बफर पर कोई कॉल करते हैं तो यह स्ट्रिंग को बदल देगा । बहुत सावधान रहें।

मेरी सलाह है कि आप आधिकारिक विधि से रहें। एक कॉपी करना इतना महंगा नहीं है और यह असुरक्षित की बुराइयों के लायक नहीं है। यदि प्रतिलिपि बनाने के लिए स्ट्रिंग बहुत बड़ी है, तो आपको इसे स्ट्रिंग में नहीं बनाना चाहिए।


धन्यवाद, यह एक बहुत विस्तृत जवाब है। "अच्छा" तरीका लगभग @ सोनिया के उत्तर के बराबर भी लगता है (क्योंकि buf.String सिर्फ कास्ट आंतरिक रूप से करता है)।
djd

1
और यह मेरे संस्करण के साथ भी काम नहीं करता है, ऐसा लगता है कि & से नहीं मिल सकता है। Go1 का उपयोग करना।
sinni800

@ sinni800 टिप के लिए धन्यवाद। मैं भूल गया कि फ़ंक्शन रिटर्न पता योग्य नहीं थे। यह अब तय हो गया है।
स्टीफन वेनबर्ग

3
अच्छी तरह से कंप्यूटर बाइट्स के ब्लॉक कॉपी करने में बहुत तेज़ हैं। और यह एक http अनुरोध है, मैं एक ऐसे परिदृश्य की कल्पना नहीं कर सकता, जहाँ संचरण विलंबता एक स्क्वील बार नहीं होगी जो बाइट सरणी की प्रतिलिपि बनाने में लगने वाले तुच्छ समय से बड़ा है। कोई भी कार्यात्मक भाषा इस प्रकार के अपरिवर्तनीय सामान को पूरे स्थान पर कॉपी करती है, और फिर भी बहुत तेजी से चलती है।
देखें

यह उत्तर पुराना है। strings.Builderयह अंतर्निहित रूप से []byteलीक होने को सुनिश्चित करने के लिए कुशलतापूर्वक करता है , और stringएक प्रतिलिपि के बिना परिवर्तित करने का समर्थन करता है जो आगे जाकर समर्थित होगा। यह 2012 में मौजूद नहीं था। @ 1.10 के बाद से डाइमेन्स्की का घोल सही हो गया है। कृपया एक संपादन पर विचार करें!
नूनो

102

अब तक के उत्तर प्रश्न के "संपूर्ण स्ट्रीम" भाग को संबोधित नहीं करते हैं। मुझे लगता है कि ऐसा करने का अच्छा तरीका है ioutil.ReadAll। आपके io.ReaderCloserनाम के साथ rc, मैं लिखूंगा,

if b, err := ioutil.ReadAll(rc); err == nil {
    return string(b)
} ...

2
धन्यवाद, अच्छा जवाब। ऐसा लगता है कि buf.ReadFrom()ईओएफ तक पूरी धारा को भी पढ़ता है।
djd

8
कैसे हास्यास्पद: मैं बस के कार्यान्वयन को पढ़ने ioutil.ReadAll()और यह बस एक लपेटता bytes.Bufferहै ReadFrom। और बफ़र की String()विधि कास्टिंग के आसपास एक सरल आवरण है string- इसलिए दोनों दृष्टिकोण व्यावहारिक रूप से समान हैं!
djd

1
यह सबसे अच्छा, सबसे संक्षिप्त समाधान है।
mk12

1
मैंने यह किया और यह काम करता है ... पहली बार। स्ट्रिंग पढ़ने के बाद किसी कारण से, अनुक्रम पढ़ता है एक खाली स्ट्रिंग लौटाता है। अभी तक यकीन नहीं हुआ।
Aldo 'xoen' Giambelluca

1
@ Aldo'xoen'Giambelluca ReadAll पाठक को खा जाता है, इसलिए अगली कॉल पर पढ़ने के लिए कुछ नहीं बचा है।
DanneJ


5

सबसे कुशल तरीका हमेशा के []byteबजाय उपयोग करना होगाstring

यदि आपको डेटा प्राप्त करने की आवश्यकता होती है io.ReadCloser, तो fmtपैकेज संभाल सकता है []byte, लेकिन यह कुशल नहीं है क्योंकि fmtकार्यान्वयन आंतरिक रूप से परिवर्तित []byteहो जाएगा string। इस रूपांतरण से बचने के लिए, आप fmt.Formatterइंटरफ़ेस को एक प्रकार के लिए लागू कर सकते हैं type ByteSlice []byte


क्या [] बाइट से स्ट्रिंग तक रूपांतरण महंगा है? मैंने माना कि स्ट्रिंग ([] बाइट) वास्तव में [] बाइट की नकल नहीं करता है, लेकिन सिर्फ स्लाइस तत्वों की एक श्रृंखला के रूप में व्याख्या करता है। यही कारण है कि मैंने बफ़र का सुझाव दिया था। स्ट्रिंग () साप्ताहिकgolang.org/src/pkg/bytes/buffer.go?s=1787:1819#L37 । मुझे लगता है कि यह जानना अच्छा होगा कि स्ट्रिंग ([] बाइट) को क्या कहा जाता है।
नट

4
से रूपांतरण []byteकरने के लिए stringयथोचित तेजी से है, लेकिन सवाल "सबसे कारगर तरीका" के बारे में पूछ रहा था। वर्तमान में, जाओ रन-टाइम हमेशा एक नया आवंटित करेगा stringजब परिवर्तित []byteकरने के लिए string। इसका कारण यह है कि संकलक को यह निर्धारित करने का तरीका नहीं पता है कि []byteरूपांतरण के बाद इसे संशोधित किया जाएगा या नहीं । संकलक अनुकूलन के लिए यहाँ कुछ जगह है।

3
func copyToString(r io.Reader) (res string, err error) {
    var sb strings.Builder
    if _, err = io.Copy(&sb, r); err == nil {
        res = sb.String()
    }
    return
}


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