क्या C ++ स्टैण्डर्ड टेम्प्लेट लाइब्रेरी क्लास है जो C # के StringBuilder या Java के StringBuffer के समान कुशल स्ट्रिंग कॉन्सेप्टेशन कार्यक्षमता प्रदान करता है ?
क्या C ++ स्टैण्डर्ड टेम्प्लेट लाइब्रेरी क्लास है जो C # के StringBuilder या Java के StringBuffer के समान कुशल स्ट्रिंग कॉन्सेप्टेशन कार्यक्षमता प्रदान करता है ?
जवाबों:
ध्यान दें कि इस उत्तर पर हाल ही में कुछ ध्यान दिया गया है। मैं इसे एक समाधान के रूप में वकालत नहीं कर रहा हूं (यह एक ऐसा समाधान है जो मैंने अतीत में देखा है, एसटीएल से पहले)। यह एक दिलचस्प दृष्टिकोण है और इसे केवल तभी लागू किया जाना चाहिए 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 से पहले थर्ड पार्टी स्ट्रिंग लाइब्रेरी के साथ किया जाता था, यह बहुत पहले की बात है। यदि आप इस प्रोफाइल जैसी रणनीति अपनाते हैं तो पहले अपना आवेदन करें।
scratch
स्ट्रिंग वास्तव में यहाँ कुछ भी पूरा करती है। मुख्य स्ट्रिंग की वास्तविकताओं की संख्या मोटे तौर पर अंतिम आकार की एक फ़ंक्शन होने जा रही है, न कि परिशिष्ट परिचालनों की संख्या, जब तक कि string
कार्यान्वयन वास्तव में खराब न हो (अर्थात, घातीय वृद्धि का उपयोग नहीं करता है)। इसलिए "बैचिंग" append
मदद नहीं करता है क्योंकि एक बार अंतर्निहित होने के बाद string
यह केवल कभी-कभी ही बढ़ेगा। शीर्ष पर यह निरर्थक कॉपी ऑपरेशंस का एक गुच्छा जोड़ता है, और जब से आप एक छोटी स्ट्रिंग के लिए अपील कर रहे हैं तब से अधिक reallocations (इसलिए कॉल new
/ / delete
) हो सकता है ।
str.reserve(1024);
इस चीज़ की तुलना में तेज़ होगा
C ++ तरीका std :: stringstream या सिर्फ सादे स्ट्रिंग कॉन्ट्रासेप्शन का उपयोग करना होगा । C ++ स्ट्रेंथ म्यूट होते हैं, इसलिए कॉन्फैक्शन की परफॉरमेंस परिकल्पना एक चिंता का विषय है।
फ़ॉर्मेटिंग के संबंध में, आप स्ट्रीम पर सभी समान फ़ॉर्मेटिंग कर सकते हैं, लेकिन एक अलग तरीके से, इसी तरहcout
। या आप एक दृढ़ता से टाइप किए गए फ़ंक्टर का उपयोग कर सकते हैं जो इसे एनकैप्सुलेट करता है और एक स्ट्रिंग प्रदान करता है। इंटरफ़ेस जैसा उदाहरण बढ़ावा देता है :: प्रारूप
StringBuilder
मौजूद है जावा की अपरिवर्तनीय बुनियादी स्ट्रिंग प्रकार की अक्षमता को कवर करने के लिए । दूसरे शब्दों में StringBuilder
, पैचवर्क है, इसलिए हमें खुशी होनी चाहिए कि हमें C ++ में ऐसी कक्षा की आवश्यकता नहीं है।
O(n)
सामान्य रूप से अपेंडिक्स की एक श्रृंखला समाप्त हो जाती है ।
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();
आप बस समवर्ती तारों के लिए .append () का उपयोग कर सकते हैं।
std::string s = "string1";
s.append("string2");
मुझे लगता है कि आप भी कर सकते हैं:
std::string s = "string1";
s += "string2";
जैसा कि C # के स्वरूपण कार्यों के लिए StringBuilder
, मेरा मानना है snprintf
(या sprintf
यदि आप एक वर्ण सरणी में छोटी गाड़ी कोड ;-) लिखना) को जोखिम में डालना चाहते हैं और वापस स्ट्रिंग में परिवर्तित करना एकमात्र विकल्प है।
चूंकि std::string
C ++ परस्पर है, आप इसका उपयोग कर सकते हैं। यह एक += operator
और एक append
समारोह है।
यदि आपको संख्यात्मक डेटा को जोड़ने की आवश्यकता है तो std::to_string
फ़ंक्शन का उपयोग करें ।
यदि आप किसी वस्तु को किसी स्ट्रिंग में क्रमबद्ध करने में सक्षम होने के रूप में और भी अधिक लचीलापन चाहते हैं तो std::stringstream
कक्षा का उपयोग करें । लेकिन आपको अपने स्वयं के कस्टम कक्षाओं के साथ काम करने के लिए अपने स्वयं के स्ट्रीमिंग ऑपरेटर कार्यों को लागू करने की आवश्यकता होगी।
सी ++ के लिए एक सुविधाजनक स्ट्रिंग बिल्डर
जैसे कई लोगों ने पहले उत्तर दिया, 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
इस तरह से व्यवहार क्यों नहीं करता है।
रस्सी यदि गंतव्य स्ट्रिंग के यादृच्छिक जगह में या एक लंबी चार दृश्यों के लिए सम्मिलित करने के लिए / हटाने स्ट्रिंग है कंटेनर लायक हो सकता है। यहाँ 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.
मैं निम्नलिखित के कारण कुछ नया जोड़ना चाहता था:
पहले अटेम्प में मैं हराने में असफल रहा
std::ostringstream
की operator<<
दक्षता, लेकिन अधिक अटैक्स के साथ मैं एक स्ट्रिंगबुलस्टाइल बनाने में सक्षम था जो कुछ मामलों में तेज है।
हर बार जब मैं एक स्ट्रिंग संलग्न करता हूं तो मैं इसे कहीं न कहीं एक संदर्भ में संग्रहीत करता हूं और कुल आकार के काउंटर को बढ़ाता हूं।
वास्तविक तरीका जिसे मैंने अंत में लागू किया है (डरावना!) एक अपारदर्शी बफर (std :: वेक्टर <char>) का उपयोग करना है:
बाइट के लिए []
स्थानांतरित तार के साथ (तार जुड़े हुए std::move
)
std::string
वस्तु का सूचक (हमारे पास स्वामित्व है)तार के लिए
std::string
वस्तु का सूचक (कोई स्वामित्व नहीं)एक छोटा सा अनुकूलन भी है, अगर अंतिम डाला गया स्ट्रिंग में चल रहा था, यह मुफ्त आरक्षित के लिए जाँच करता है लेकिन अप्रयुक्त बाइट्स और अपारदर्शी बफर का उपयोग करने के बजाय आगे बाइट्स की दुकान करता है (यह कुछ मेमोरी को बचाने के लिए है, यह वास्तव में इसे थोड़ा धीमा कर देता है , शायद सीपीयू पर भी निर्भर करता है, और वैसे भी अतिरिक्त आरक्षित स्थान के साथ तार देखना दुर्लभ है)
यह अंत में की तुलना में थोड़ा तेज था, std::ostringstream
लेकिन इसमें कुछ गिरावट आई है:
ostringstream
निष्कर्ष? उपयोग
std::ostringstream
यह पहले से ही सबसे बड़ी अड़चन को ठीक करता है जबकि खान कार्यान्वयन के साथ गति में कुछ% अंक हासिल करना गिरावट के लायक नहीं है।
std::ostringstream
।