Apakah ada kelas C ++ Standard Template Library yang menyediakan serangkaian fungsi Rangkaian efisien, mirip dengan C # 's StringBuilder atau Jawa StringBuffer ?
Apakah ada kelas C ++ Standard Template Library yang menyediakan serangkaian fungsi Rangkaian efisien, mirip dengan C # 's StringBuilder atau Jawa StringBuffer ?
Jawaban:
Perhatikan jawaban ini baru-baru ini mendapat perhatian. Saya tidak menganjurkan ini sebagai solusi (ini adalah solusi yang saya lihat di masa lalu, sebelum STL). Ini adalah pendekatan yang menarik dan hanya boleh diterapkan di atas std::string
atau std::stringstream
jika setelah membuat profil kode Anda Anda menemukan ini membuat perbaikan.
Saya biasanya menggunakan salah satu std::string
atau std::stringstream
. Saya tidak pernah punya masalah dengan ini. Saya biasanya akan memesan kamar terlebih dahulu jika saya tahu ukuran kasar tali sebelumnya.
Saya telah melihat orang lain membuat pembuat string yang dioptimalkan sendiri di masa lalu.
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;
}
};
Ini menggunakan dua string satu untuk sebagian besar string dan yang lainnya sebagai area awal untuk merangkai string pendek. Ini mengoptimalkan penambahan dengan cara batching operasi penambahan pendek dalam satu string kecil kemudian menambahkan ini ke string utama, sehingga mengurangi jumlah realokasi yang diperlukan pada string utama saat menjadi lebih besar.
Saya belum meminta trik ini dengan std::string
atau std::stringstream
. Saya pikir itu digunakan dengan perpustakaan string pihak ketiga sebelum std :: string, itu sudah lama sekali. Jika Anda mengadopsi strategi seperti profil ini, aplikasi Anda terlebih dahulu.
scratch
string benar-benar menyelesaikan apa pun di sini. Jumlah realokasi string utama sebagian besar akan menjadi fungsi dari ukuran finalnya, bukan jumlah operasi tambahan, kecuali string
implementasinya benar-benar buruk (yaitu, tidak menggunakan pertumbuhan eksponensial). Jadi, "menumpuk" append
tidak akan membantu karena begitu dasarnya string
itu besar itu hanya akan tumbuh sesekali. Selain itu, ia menambahkan banyak operasi penyalinan yang redundan, dan mungkin lebih banyak realokasi (karena itu panggilan ke new
/ delete
) karena Anda menambahkan string pendek.
str.reserve(1024);
akan lebih cepat dari hal ini
Cara C ++ adalah dengan menggunakan std :: stringstream atau hanya gabungan string sederhana. String C ++ bisa berubah sehingga pertimbangan kinerja gabungan tidak terlalu menjadi perhatian.
berkenaan dengan pemformatan, Anda dapat melakukan semua pemformatan yang sama pada aliran, tetapi dengan cara yang berbeda, mirip dengancout
. atau Anda dapat menggunakan functor yang sangat diketik yang merangkum ini dan menyediakan antarmuka seperti String.Format misalnya boost :: format
StringBuilder
ada adalah untuk menutupi ketidakefisienan tipe String dasar Java yang tidak berubah . Dengan kata lain StringBuilder
adalah tambal sulam, jadi kita harus senang kita tidak perlu kelas seperti itu di C ++.
O(n)
pada umumnya.
The std::string.append
fungsi bukan pilihan yang baik karena tidak menerima berbagai bentuk data. Alternatif yang lebih bermanfaat adalah menggunakan std::stringstream
; seperti itu:
#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();
Anda dapat menggunakan .append () untuk string yang digabungkan.
std::string s = "string1";
s.append("string2");
Saya pikir Anda bahkan dapat melakukannya:
std::string s = "string1";
s += "string2";
Adapun operasi pemformatan C # StringBuilder
, saya percaya snprintf
(atau sprintf
jika Anda ingin mengambil risiko menulis kode kereta ;-)) ke dalam array karakter dan mengkonversi kembali ke string adalah tentang satu-satunya pilihan.
Karena std::string
dalam C ++ bisa berubah, Anda dapat menggunakannya. Ini memiliki += operator
dan append
fungsi.
Jika Anda perlu menambahkan data numerik gunakan std::to_string
fungsi.
Jika Anda menginginkan fleksibilitas yang lebih besar dalam bentuk mampu membuat serialisasi objek apa pun menjadi string, gunakan std::stringstream
kelas. Tetapi Anda harus mengimplementasikan fungsi operator streaming Anda sendiri agar dapat bekerja dengan kelas kustom Anda sendiri.
Pembuat string yang nyaman untuk c ++
Seperti banyak orang yang menjawab sebelumnya, std :: stringstream adalah metode pilihan. Ini berfungsi baik dan memiliki banyak opsi konversi dan pemformatan. IMO memiliki satu kelemahan yang cukup merepotkan: Anda tidak dapat menggunakannya sebagai satu liner atau sebagai ekspresi. Anda selalu harus menulis:
std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );
yang cukup menjengkelkan, terutama ketika Anda ingin menginisialisasi string dalam konstruktor.
Alasannya adalah, bahwa a) std :: stringstream tidak memiliki operator konversi ke std :: string dan b) operator dari stringstream tidak mengembalikan referensi stringstream, tetapi sebaliknya std :: ostream reference - yang tidak dapat dihitung lebih lanjut sebagai aliran string.
Solusinya adalah mengesampingkan std :: stringstream dan memberikannya operator pencocokan yang lebih baik:
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;
}
Dengan ini, Anda dapat menulis hal-hal seperti
std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )
bahkan di konstruktor.
Saya harus mengakui bahwa saya tidak mengukur kinerja, karena saya belum menggunakannya di lingkungan yang banyak menggunakan pembuatan string, tapi saya berasumsi itu tidak akan jauh lebih buruk daripada std :: stringstream, karena semuanya sudah selesai melalui referensi (kecuali konversi ke string, tapi itu operasi salinan di std :: stringstream juga)
std::stringstream
tidak berperilaku seperti ini.
The Rope wadah mungkin layak jika harus memasukkan / menghapus string ke tempat acak string tujuan atau untuk urutan arang panjang. Berikut adalah contoh dari implementasi 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.
Saya ingin menambahkan sesuatu yang baru karena hal berikut:
Pada usaha pertama saya gagal mengalahkan
std::ostringstream
ini operator<<
efisiensi, tetapi dengan lebih banyak upaya saya bisa membuat StringBuilder yang lebih cepat dalam beberapa kasus.
Setiap kali saya menambahkan string, saya hanya menyimpan referensi di suatu tempat dan menambah penghitung ukuran total.
Cara nyata saya akhirnya mengimplementasikannya (Horor!) Adalah dengan menggunakan buffer buram (std :: vector <char>):
untuk byte []
untuk string yang dipindahkan (string ditambahkan dengan std::move
)
std::string
objek (kami memiliki kepemilikan)untuk string
std::string
objek (tidak ada kepemilikan)Ada juga satu optimasi kecil, jika string yang dimasukkan terakhir dipindahkan, itu memeriksa byte gratis tetapi tidak terpakai dan menyimpan byte lebih lanjut di sana daripada menggunakan buffer buram (ini adalah untuk menghemat beberapa memori, itu sebenarnya membuatnya sedikit lebih lambat , mungkin juga bergantung pada CPU, dan jarang melihat string dengan ruang ekstra tetap)
Ini akhirnya sedikit lebih cepat daripada std::ostringstream
tetapi memiliki beberapa kelemahan:
ostringstream
kesimpulan? menggunakan
std::ostringstream
Ini sudah memperbaiki bottleneck terbesar sementara kecepatan beberapa% poin dengan implementasi tambang tidak sebanding dengan kerugiannya.
std::ostringstream
.