C # 'ın benzer verimli dize birleştirme işlevsellik sağlayan bir C ++ Standart Şablon Kütüphanesi sınıfı var mıdır StringBuilder veya Java'nın StringBuffer ?
C # 'ın benzer verimli dize birleştirme işlevsellik sağlayan bir C ++ Standart Şablon Kütüphanesi sınıfı var mıdır StringBuilder veya Java'nın StringBuffer ?
Yanıtlar:
NOT Bu cevap son zamanlarda biraz ilgi gördü. Bunu bir çözüm olarak savunmuyorum (geçmişte STL'den önce gördüğüm bir çözüm). Bu ilginç bir yaklaşımdır ve sadece üzerine uygulanmalıdır std::stringya std::stringstreamkodunuzu profilleme sonra keşfettiğiniz bu bir iyileşme sağlar.
Normalde kullanmak ya std::stringveya std::stringstream. Bunlarla hiç problem yaşamadım. Önceden dizenin kaba boyutunu biliyorsanız normalde ilk önce bazı oda rezervasyonu.
Diğer insanların uzak geçmişte kendi optimize dize yapıcılarını yaptıklarını gördüm.
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;
}
};
Biri dizenin çoğunluğu için diğeri kısa dizeleri birleştirmek için bir çizik alanı olarak iki dizge kullanır. Kısa ekleme işlemlerini küçük bir dizede topluyor ve ardından bunu ana dizeye ekleyerek, böylece ana dizede daha fazla büyüdükçe gerekli yeniden tahsis sayısını azaltarak optimizasyon yapar.
Bu numarayı std::stringveya ile istemedim std::stringstream. Ben std :: string önce bir üçüncü taraf dize kütüphanesi ile kullanıldığını düşünüyorum, bu uzun zaman önceydi. Bu profil gibi bir strateji benimserseniz, öncelikle başvurunuz.
scratchdize gerçekten burada bir şey yapar. Ana dize yeniden tahsis sayısı, stringuygulama gerçekten zayıf olmadığı sürece (yani üstel büyüme kullanmazsa) büyük ölçüde son boyutunun bir fonksiyonu olacaktır, ekleme işlemlerinin sayısı değil. Yani "yığınlama" appendyardımcı olmaz çünkü altta yatan stringbüyük bir zaman sadece her iki şekilde büyüyecektir. Bunun yanı sıra, bir sürü yedek kopya işlemi ekler ve kısa bir dizeye eklediğiniz için daha fazla yeniden konumlandırma (dolayısıyla new/ çağrıları delete) olabilir.
str.reserve(1024);bu şeyden daha hızlı olurdu
C ++ yolu std :: stringstream veya sadece düz dize birleşimlerini kullanmak olacaktır. C ++ dizeleri değiştirilebilir, böylece birleştirme performans değerlendirmeleri daha az endişe kaynağıdır.
biçimlendirmeyle ilgili olarak, bir akışta aynı biçimlendirmeyi ancak benzer şekilde farklı bir şekilde yapabilirsinizcout . ya da bunu içine alan ve bir String.Format benzeri bir arayüz sağlayan güçlü bir şekilde yazılmış bir functor kullanabilirsiniz örneğin boost :: format
StringBuildervar etmektir Java'nın değişmez temel String türü verimsizlik kapsayacak . Başka bir deyişle StringBuilderpatchwork, bu yüzden C ++ 'da böyle bir sınıfa ihtiyacımız olmadığına sevinmeliyiz.
O(n)genel olarak bir dizi ekleme sona erer .
std::string.appendO verinin birçok formları kabul etmez çünkü işlev iyi bir seçenek değildir. Daha kullanışlı bir alternatif kullanmaktır std::stringstream; şöyle:
#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();
Dizeleri birleştirmek için .append () yöntemini kullanabilirsiniz.
std::string s = "string1";
s.append("string2");
Hatta yapabileceğinizi düşünüyorum:
std::string s = "string1";
s += "string2";
C # 's biçimlendirme işlemleri gelince StringBuilder, snprintf(veya sprintfbuggy kodu ;-) yazma riskini istiyorsanız) bir karakter dizisine inanıyorum ve bir dize geri dönüştürmek tek seçenek hakkında.
Yana std::stringdeğişebilirdir C ++ bunu kullanabilir. Bir += operatorve bir appendişlevi vardır.
Sayısal veri eklemeniz gerekiyorsa std::to_stringişlevleri kullanın .
Herhangi bir nesneyi dizeye serileştirme şeklinde daha fazla esneklik istiyorsanız, std::stringstreamsınıfı kullanın . Ancak, kendi özel sınıflarınızla çalışması için kendi akış operatörü işlevlerinizi uygulamanız gerekir.
C ++ için uygun bir dize oluşturucu
Daha önce birçok kişi gibi, std :: stringstream de seçim yöntemidir. İyi çalışıyor ve çok sayıda dönüştürme ve biçimlendirme seçeneği var. IMO yine de oldukça rahatsız edici bir kusuru var: Tek bir astar veya bir ifade olarak kullanamazsınız. Her zaman şunu yazmalısınız:
std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );
özellikle yapıcıdaki dizeleri başlatmak istediğinizde oldukça can sıkıcıdır.
Bunun nedeni, a) std :: stringstream öğesinin std :: string'e dönüştürme operatörü olmaması ve b) stringstream operatörünün << () 'leri bir stringstream referansı döndürmez, bunun yerine std :: ostream referansını döndürür - bir dize akışı olarak daha fazla hesaplanamaz.
Çözüm std :: stringstream'i geçersiz kılmak ve daha iyi eşleşen işleçler vermektir:
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;
}
Bununla, böyle şeyler yazabilirsiniz
std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )
yapıcıda bile.
İtiraf etmeliyim ki, henüz dize yapmanın ağır kullanımını kullanan bir ortamda kullanmadığım için performansı ölçmedim, ama her şeyin yapıldığı için std :: stringstream'den çok daha kötü olmayacağını varsayıyorum. referanslar aracılığıyla (dizeye dönüştürme hariç, ancak std :: stringstream öğesinde de bir kopya işlemi)
std::stringstreamböyle davranmadığını anlamıyorum .
Halat hedef dizesinin rastgele yerine veya uzun karakter dizileri için / silme dize eklemek için varsa konteyner değerinde olabilir. SGI'nın uygulanmasından bir örnek:
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.
Aşağıdakiler nedeniyle yeni bir şey eklemek istedim:
İlk denemede yenemedim
std::ostringstream 'ler operator<<
verimlilik, ancak daha fazla girişimi ile bazı durumlarda daha hızlı bir StringBuilder yapabildim.
Her zaman bir dize eklemek Ben sadece bir yerde bir referans saklamak ve toplam boyut sayacı artırmak.
Nihayetinde uyguladığım gerçek yol (Korku!) Opak bir tampon (std :: vector <char>) kullanmaktır:
bayt için []
taşınan dizeler için (eklenen dizeler std::move)
std::stringnesnenin işaretçisi (sahipliğimiz var)teller için
std::stringnesneye işaretçi (sahiplik yok)Ayrıca, en son eklenen dize taşınırsa, küçük bir optimizasyon da vardır, opak tamponu kullanmak yerine ücretsiz ayrılmış ancak kullanılmayan baytları kontrol eder ve daha fazla bayt depolar (bu, biraz bellek tasarrufu sağlar, aslında biraz daha yavaş hale getirir) , belki de CPU'ya bağlıdır ve zaten fazladan ayrılmış alana sahip dizeleri görmek nadirdir)
Bu nihayet biraz daha hızlıydı std::ostringstreamama birkaç dezavantajı var:
ostringstreamSonuç? kullanım
std::ostringstream
Maden uygulamasında% birkaç hızda ganing yaparken en büyük darboğazı düzeltir, dezavantajlara değmez.
std::ostringstream.