C ++ StringBuffer / StringBuilder के बराबर?


184

क्या C ++ स्टैण्डर्ड टेम्प्लेट लाइब्रेरी क्लास है जो C # के StringBuilder या Java के StringBuffer के समान कुशल स्ट्रिंग कॉन्सेप्टेशन कार्यक्षमता प्रदान करता है ?


3
संक्षिप्त उत्तर है: हां, एसटीएल के पास इसके लिए एक वर्ग है और यह है std::ostringstream
कॉफिडेवलपर्स

अरे @andrew। क्या आप स्वीकृत उत्तर को बदल सकते हैं? एक स्पष्ट विजेता उत्तर है और यह वर्तमान स्वीकृत उत्तर नहीं है।
नल

जवाबों:


53

ध्यान दें कि इस उत्तर पर हाल ही में कुछ ध्यान दिया गया है। मैं इसे एक समाधान के रूप में वकालत नहीं कर रहा हूं (यह एक ऐसा समाधान है जो मैंने अतीत में देखा है, एसटीएल से पहले)। यह एक दिलचस्प दृष्टिकोण है और इसे केवल तभी लागू किया जाना चाहिए std::stringया std::stringstreamयदि आपके कोड को प्रोफाइल करने के बाद आपको पता चलता है कि यह सुधार करता है।

मैं सामान्य रूप से std::stringया तो उपयोग करता हूं std::stringstream। मुझे कभी इनसे कोई परेशानी नहीं हुई। अगर मैं पहले से किसी न किसी आकार को जानता हूं तो मैं सामान्य रूप से कुछ कमरा आरक्षित करूंगा।

मैंने देखा है कि अन्य लोग दूर के अतीत में अपने स्वयं के अनुकूलित स्ट्रिंग बिल्डर बनाते हैं।

class StringBuilder {
private:
    std::string main;
    std::string scratch;

    const std::string::size_type ScratchSize = 1024;  // or some other arbitrary number

public:
    StringBuilder & append(const std::string & str) {
        scratch.append(str);
        if (scratch.size() > ScratchSize) {
            main.append(scratch);
            scratch.resize(0);
        }
        return *this;
    }

    const std::string & str() {
        if (scratch.size() > 0) {
            main.append(scratch);
            scratch.resize(0);
        }
        return main;
    }
};

यह स्ट्रिंग के अधिकांश भाग के लिए दो स्ट्रिंग्स का उपयोग करता है और दूसरे को छोटे तारों को समतल करने के लिए एक स्क्रैच क्षेत्र के रूप में। यह एक छोटे से स्ट्रिंग में छोटे एपेंड ऑपरेशन को बैचने के द्वारा अनुकूलन को बढ़ावा देता है, फिर इसे मुख्य स्ट्रिंग में जोड़ देता है, इस प्रकार मुख्य स्ट्रिंग पर आवश्यक reallocations की संख्या को कम करता है क्योंकि यह बड़ा हो जाता है।

मुझे इस ट्रिक के साथ std::stringया इसकी आवश्यकता नहीं है std::stringstream। मुझे लगता है कि इसका उपयोग std :: string से पहले थर्ड पार्टी स्ट्रिंग लाइब्रेरी के साथ किया जाता था, यह बहुत पहले की बात है। यदि आप इस प्रोफाइल जैसी रणनीति अपनाते हैं तो पहले अपना आवेदन करें।


13
पहिया बदलते। std :: stringstream उचित उत्तर है। नीचे अच्छे उत्तर देखें।
कोबोर 42

12
@ Kobor42 मैं आपके साथ सहमत हूं क्योंकि मैं अपने उत्तर की पहली और अंतिम पंक्ति पर इंगित करता हूं।
आईएन

1
मुझे नहीं लगता कि scratchस्ट्रिंग वास्तव में यहाँ कुछ भी पूरा करती है। मुख्य स्ट्रिंग की वास्तविकताओं की संख्या मोटे तौर पर अंतिम आकार की एक फ़ंक्शन होने जा रही है, न कि परिशिष्ट परिचालनों की संख्या, जब तक कि stringकार्यान्वयन वास्तव में खराब न हो (अर्थात, घातीय वृद्धि का उपयोग नहीं करता है)। इसलिए "बैचिंग" appendमदद नहीं करता है क्योंकि एक बार अंतर्निहित होने के बाद stringयह केवल कभी-कभी ही बढ़ेगा। शीर्ष पर यह निरर्थक कॉपी ऑपरेशंस का एक गुच्छा जोड़ता है, और जब से आप एक छोटी स्ट्रिंग के लिए अपील कर रहे हैं तब से अधिक reallocations (इसलिए कॉल new/ / delete) हो सकता है ।
BeeOnRope

@BeeOnRope मैं आपसे सहमत हूं।
आईएन

मुझे पूरा यकीन है कि str.reserve(1024);इस चीज़ की तुलना में तेज़ होगा
पिछलग्गू

160

C ++ तरीका std :: stringstream या सिर्फ सादे स्ट्रिंग कॉन्ट्रासेप्शन का उपयोग करना होगा । C ++ स्ट्रेंथ म्यूट होते हैं, इसलिए कॉन्फैक्शन की परफॉरमेंस परिकल्पना एक चिंता का विषय है।

फ़ॉर्मेटिंग के संबंध में, आप स्ट्रीम पर सभी समान फ़ॉर्मेटिंग कर सकते हैं, लेकिन एक अलग तरीके से, इसी तरहcout । या आप एक दृढ़ता से टाइप किए गए फ़ंक्टर का उपयोग कर सकते हैं जो इसे एनकैप्सुलेट करता है और एक स्ट्रिंग प्रदान करता है। इंटरफ़ेस जैसा उदाहरण बढ़ावा देता है :: प्रारूप


59
C ++ स्ट्रिंग्स परस्पर हैं : वास्तव में। संपूर्ण कारण StringBuilderमौजूद है जावा की अपरिवर्तनीय बुनियादी स्ट्रिंग प्रकार की अक्षमता को कवर करने के लिए । दूसरे शब्दों में StringBuilder, पैचवर्क है, इसलिए हमें खुशी होनी चाहिए कि हमें C ++ में ऐसी कक्षा की आवश्यकता नहीं है।
बोब्बोबो

55
@bobobobo अपरिवर्तनीय तार के अन्य लाभ हैं, हालांकि, पाठ्यक्रमों के लिए इसके घोड़े
जे.के.

8
सादे स्ट्रिंग संघनन एक नई वस्तु नहीं बनाते हैं, इसलिए जावा में अपरिवर्तनीयता के साथ भी यही समस्या है? विचार करें कि सभी चर निम्नलिखित उदाहरण में हैं: a = b + c + d + e + f; क्या यह ऑपरेटर को b और c, फिर ऑपरेटर + को परिणाम और d, आदि पर कॉल करने वाला नहीं है?
सर्ज रोजै

9
एक मिनट लोगों को पकड़ो, मानक स्ट्रिंग वर्ग जानता है कि खुद को कैसे बदलना है लेकिन इसका मतलब यह नहीं है कि अक्षमता नहीं है। जहाँ तक मुझे पता है std :: string केवल इसके आंतरिक चार के आकार को बढ़ा नहीं सकती है *। इसका मतलब है कि इसे एक तरह से बदलना जो अधिक वर्णों की आवश्यकता होती है, एक वास्तविक और नकल की आवश्यकता होती है। यह चेरों के एक वेक्टर से अलग नहीं है और उस मामले में आपको जिस स्थान की आवश्यकता है उसे आरक्षित करना निश्चित रूप से बेहतर है।
ट्राएगवे स्कोशोलम

7
@TrygveSkogsholm - यह चार्म के एक वेक्टर से अलग नहीं है, लेकिन निश्चित रूप से स्ट्रिंग की "क्षमता" इसके आकार से बड़ी हो सकती है, इसलिए सभी एपेंडेस को एक वास्तविक स्थान की आवश्यकता नहीं है। सामान्य तार में एक घातीय वृद्धि की रणनीति का उपयोग करेंगे ताकि संलग्न अभी भी एक रैखिक लागत ऑपरेशन के लिए amortizes। यह जावा के अपरिवर्तनीय स्ट्रिंग्स से अलग है जिसमें प्रत्येक अपेंडेंस ऑपरेशन को दोनों स्ट्रिंग्स में सभी पात्रों को एक नए सिरे से कॉपी करने की आवश्यकता होती है, इसलिए O(n)सामान्य रूप से अपेंडिक्स की एक श्रृंखला समाप्त हो जाती है ।
मधुमक्खी पालन

93

std::string.appendसमारोह एक अच्छा विकल्प है क्योंकि यह डेटा के कई रूपों को स्वीकार नहीं करता नहीं है। उपयोग करने के लिए एक अधिक उपयोगी विकल्प है std::stringstream; इस तरह:

#include <sstream>
// ...

std::stringstream ss;

//put arbitrary formatted data into the stream
ss << 4.5 << ", " << 4 << " whatever";

//convert the stream buffer into a string
std::string str = ss.str();

43

std::string है सी ++ बराबर: यह परिवर्तनशील है।


13

आप बस समवर्ती तारों के लिए .append () का उपयोग कर सकते हैं।

std::string s = "string1";
s.append("string2");

मुझे लगता है कि आप भी कर सकते हैं:

std::string s = "string1";
s += "string2";

जैसा कि C # के स्वरूपण कार्यों के लिए StringBuilder, मेरा मानना ​​है snprintf(या sprintfयदि आप एक वर्ण सरणी में छोटी गाड़ी कोड ;-) लिखना) को जोखिम में डालना चाहते हैं और वापस स्ट्रिंग में परिवर्तित करना एकमात्र विकल्प है।


प्रिंटफ या .NET के स्ट्रिंग के रूप में उसी तरह से नहीं। हालांकि, वे हैं?
एंडी शेलम

1
यह कहने के लिए थोड़ा असंतुष्ट हैं कि वे एकमात्र तरीका हैं
jk।

2
@jk - वे एकमात्र तरीका है जब .NET के स्ट्रिंगबर्स्ट की प्रारूपण क्षमता की तुलना की जाती है, जो कि मूल प्रश्न विशेष रूप से पूछा जाता है। मैंने कहा "मुझे विश्वास है" इसलिए मैं गलत हो सकता हूं, लेकिन क्या आप मुझे प्रिंटफ़ का उपयोग किए बिना सी ++ में स्ट्रिंगब्यूरी की कार्यक्षमता प्राप्त करने का एक तरीका दिखा सकते हैं?
एंडी शेलम

कुछ वैकल्पिक स्वरूपण विकल्पों को शामिल करने के लिए मेरे उत्तर को अपडेट किया
jk।

6

चूंकि std::stringC ++ परस्पर है, आप इसका उपयोग कर सकते हैं। यह एक += operatorऔर एक appendसमारोह है।

यदि आपको संख्यात्मक डेटा को जोड़ने की आवश्यकता है तो std::to_stringफ़ंक्शन का उपयोग करें ।

यदि आप किसी वस्तु को किसी स्ट्रिंग में क्रमबद्ध करने में सक्षम होने के रूप में और भी अधिक लचीलापन चाहते हैं तो std::stringstreamकक्षा का उपयोग करें । लेकिन आपको अपने स्वयं के कस्टम कक्षाओं के साथ काम करने के लिए अपने स्वयं के स्ट्रीमिंग ऑपरेटर कार्यों को लागू करने की आवश्यकता होगी।


4

std :: string का + = const char * के साथ काम नहीं करता है (क्या सामान जो "स्ट्रिंग को जोड़ने के लिए" जैसा प्रतीत होता है), इसलिए निश्चित रूप से स्ट्रिंगस्ट्रीम का उपयोग करना आवश्यक है जो निकटतम है - आप बस उपयोग करते हैं << + के बजाय


3

सी ++ के लिए एक सुविधाजनक स्ट्रिंग बिल्डर

जैसे कई लोगों ने पहले उत्तर दिया, std :: stringstream तरीका है पसंद का। यह अच्छा काम करता है और इसमें बहुत सारे रूपांतरण और प्रारूपण विकल्प हैं। IMO में इसका एक बहुत असुविधाजनक दोष है हालांकि: आप इसे एक लाइनर के रूप में या एक अभिव्यक्ति के रूप में उपयोग नहीं कर सकते। आपको हमेशा लिखना होगा:

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );

जो बहुत कष्टप्रद है, खासकर जब आप कंस्ट्रक्टर में स्ट्रिंग्स को इनिशियलाइज़ करना चाहते हैं।

इसका कारण यह है कि a) std :: stringstream का कोई रूपांतरण ऑपरेटर std :: string और b) ऑपरेटर नहीं है << () स्ट्रिंग का संदर्भ स्ट्रिंग के संदर्भ में नहीं लौटता है, लेकिन एक std :: ostream संदर्भ के बजाय - जिसे एक स्ट्रिंग स्ट्रीम के रूप में आगे गणना नहीं की जा सकती है।

समाधान std :: stringstream को ओवरराइड करने और इसे बेहतर मिलान ऑपरेटरों को देने के लिए है:

namespace NsStringBuilder {
template<typename T> class basic_stringstream : public std::basic_stringstream<T>
{
public:
    basic_stringstream() {}

    operator const std::basic_string<T> () const                                { return std::basic_stringstream<T>::str();                     }
    basic_stringstream<T>& operator<<   (bool _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (char _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (signed char _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned char _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (short _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned short _val)                   { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (int _val)                              { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned int _val)                     { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long long _val)                        { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long long _val)               { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (float _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (double _val)                           { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long double _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (void* _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::streambuf* _val)                  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ostream& (*_val)(std::ostream&))  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios& (*_val)(std::ios&))          { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios_base& (*_val)(std::ios_base&)){ std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (const T* _val)                         { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val)); }
    basic_stringstream<T>& operator<<   (const std::basic_string<T>& _val)      { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val.c_str())); }
};

typedef basic_stringstream<char>        stringstream;
typedef basic_stringstream<wchar_t>     wstringstream;
}

इसके साथ, आप जैसी चीजें लिख सकते हैं

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )

कंस्ट्रक्टर में भी।

मुझे कबूल करना है कि मैंने प्रदर्शन को नहीं मापा है, क्योंकि मैंने इसे ऐसे वातावरण में उपयोग नहीं किया है जो स्ट्रिंग निर्माण का भारी उपयोग करता है, लेकिन मुझे लगता है कि यह std :: stringstream की तुलना में बहुत खराब नहीं होगा, क्योंकि सब कुछ किया जाता है संदर्भों के माध्यम से (स्ट्रिंग में रूपांतरण को छोड़कर, लेकिन std में एक प्रतिलिपि ऑपरेशन को लागू करता है :: stringstream भी)


यह साफ-सुथरा है। मैं नहीं देखता कि std::stringstreamइस तरह से व्यवहार क्यों नहीं करता है।
19p में einpoklum

1

रस्सी यदि गंतव्य स्ट्रिंग के यादृच्छिक जगह में या एक लंबी चार दृश्यों के लिए सम्मिलित करने के लिए / हटाने स्ट्रिंग है कंटेनर लायक हो सकता है। यहाँ SGI के कार्यान्वयन से एक उदाहरण दिया गया है:

crope r(1000000, 'x');          // crope is rope<char>. wrope is rope<wchar_t>
                                // Builds a rope containing a million 'x's.
                                // Takes much less than a MB, since the
                                // different pieces are shared.
crope r2 = r + "abc" + r;       // concatenation; takes on the order of 100s
                                // of machine instructions; fast
crope r3 = r2.substr(1000000, 3);       // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
                                // correct, but slow; may take a
                                // minute or more.

0

मैं निम्नलिखित के कारण कुछ नया जोड़ना चाहता था:

पहले अटेम्प में मैं हराने में असफल रहा

std::ostringstream की operator<<

दक्षता, लेकिन अधिक अटैक्स के साथ मैं एक स्ट्रिंगबुलस्टाइल बनाने में सक्षम था जो कुछ मामलों में तेज है।

हर बार जब मैं एक स्ट्रिंग संलग्न करता हूं तो मैं इसे कहीं न कहीं एक संदर्भ में संग्रहीत करता हूं और कुल आकार के काउंटर को बढ़ाता हूं।

वास्तविक तरीका जिसे मैंने अंत में लागू किया है (डरावना!) एक अपारदर्शी बफर (std :: वेक्टर <char>) का उपयोग करना है:

  • 1 बाइट हैडर (यदि डेटा निम्नलिखित है तो यह बताने के लिए 2 बिट्स: स्थानांतरित स्ट्रिंग, स्ट्रिंग या बाइट []]
  • बाइट के बारे में बताने के लिए 6 बिट्स []

बाइट के लिए []

  • मैं छोटी तारों के सीधे बाइट्स (अनुक्रमिक मेमोरी एक्सेस के लिए) स्टोर करता हूं

स्थानांतरित तार के साथ (तार जुड़े हुए std::move)

  • किसी std::stringवस्तु का सूचक (हमारे पास स्वामित्व है)
  • यदि वहाँ अप्रयुक्त आरक्षित बाइट्स हैं तो कक्षा में एक ध्वज स्थापित करें

तार के लिए

  • किसी std::stringवस्तु का सूचक (कोई स्वामित्व नहीं)

एक छोटा सा अनुकूलन भी है, अगर अंतिम डाला गया स्ट्रिंग में चल रहा था, यह मुफ्त आरक्षित के लिए जाँच करता है लेकिन अप्रयुक्त बाइट्स और अपारदर्शी बफर का उपयोग करने के बजाय आगे बाइट्स की दुकान करता है (यह कुछ मेमोरी को बचाने के लिए है, यह वास्तव में इसे थोड़ा धीमा कर देता है , शायद सीपीयू पर भी निर्भर करता है, और वैसे भी अतिरिक्त आरक्षित स्थान के साथ तार देखना दुर्लभ है)

यह अंत में की तुलना में थोड़ा तेज था, std::ostringstreamलेकिन इसमें कुछ गिरावट आई है:

  • मैंने निश्चित लैंथ चार प्रकार (इसलिए 1,2 या 4 बाइट्स, यूटीएफ 8 के लिए अच्छा नहीं) मान लिया, मैं यह नहीं कह रहा हूं कि यह यूटीएफ 8 के लिए काम नहीं करेगा, बस मैंने इसे आलस्य के लिए जाँच नहीं किया है।
  • मैंने खराब कोडिंग अभ्यास का इस्तेमाल किया (अपारदर्शी बफर, इसे पोर्टेबल नहीं बनाने के लिए आसान, मेरा मानना ​​है कि मेरा पोर्टेबल तरीका है)
  • की सभी सुविधाओं को कम कर देता है ostringstream
  • यदि कुछ संदर्भित स्ट्रिंग को विलय से पहले हटा दिया जाता है तो सभी तार: अपरिभाषित व्यवहार।

निष्कर्ष? उपयोग std::ostringstream

यह पहले से ही सबसे बड़ी अड़चन को ठीक करता है जबकि खान कार्यान्वयन के साथ गति में कुछ% अंक हासिल करना गिरावट के लायक नहीं है।

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