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::string
ya std::stringstream
kodunuzu profilleme sonra keşfettiğiniz bu bir iyileşme sağlar.
Normalde kullanmak ya std::string
veya 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::string
veya 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.
scratch
dize gerçekten burada bir şey yapar. Ana dize yeniden tahsis sayısı, string
uygulama 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" append
yardımcı olmaz çünkü altta yatan string
bü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
StringBuilder
var etmektir Java'nın değişmez temel String türü verimsizlik kapsayacak . Başka bir deyişle StringBuilder
patchwork, 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.append
O 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 sprintf
buggy 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::string
değişebilirdir C ++ bunu kullanabilir. Bir += operator
ve bir append
işlevi vardır.
Sayısal veri eklemeniz gerekiyorsa std::to_string
işlevleri kullanın .
Herhangi bir nesneyi dizeye serileştirme şeklinde daha fazla esneklik istiyorsanız, std::stringstream
sı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::stringstream
bö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::string
nesnenin işaretçisi (sahipliğimiz var)teller için
std::string
nesneye 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::ostringstream
ama birkaç dezavantajı var:
ostringstream
Sonuç? 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
.