Saya tidak percaya tugas rutin ini seperti sakit kepala di c ++
wfbarksdale
6
Ini bukan sakit kepala di c ++ - ada berbagai cara untuk mencapainya. programmer kurang mengetahui c ++ daripada c # - ini tentang pemasaran dan investasi ... lihat ini untuk berbagai opsi c ++ untuk mencapai hal yang sama: cplusplus.com/faq/
followingences
9
@ hB0 melalui banyak pertanyaan jawaban dan masih belum memutuskan cara adalah sakit kepala. yang satu membutuhkan perpustakaan itu, yang lain hanya untuk ruang, yang lain tidak menangani ruang ..
Mengapa segala sesuatu di C ++ harus menjadi perjuangan?
Wael Assaf
Jawaban:
145
Algoritma perpustakaan standar C ++ cukup universal berbasis di sekitar iterator daripada wadah beton. Sayangnya ini membuatnya sulit untuk menyediakan splitfungsi mirip Java di pustaka standar C ++, meskipun tidak ada yang berpendapat bahwa ini akan lebih mudah. Tapi seperti apa tipe pengembaliannya?std::vector<std::basic_string<…>>? Mungkin, tapi kemudian kami terpaksa melakukan alokasi (berpotensi berlebihan dan mahal).
Alih-alih, C ++ menawarkan sejumlah besar cara untuk membagi string berdasarkan pembatas yang kompleks secara arbitrer, tetapi tidak satupun dari mereka yang dienkapsulasi sebaik di bahasa lain. Banyak cara mengisi seluruh posting blog .
Paling sederhana, Anda bisa beralih menggunakan std::string::findsampai Anda menekan std::string::npos, dan ekstrak konten menggunakan std::string::substr.
Versi yang lebih lancar (dan idiomatik, tetapi mendasar) untuk membelah di whitespace akan menggunakan std::istringstream:
auto iss = std::istringstream{"The quick brown fox"};auto str = std::string{};while(iss >> str){
process(str);}
Menggunakan std::istream_iterators , isi dari stream string juga dapat disalin ke dalam vektor menggunakan konstruktor rentang iteratornya.
Beberapa perpustakaan (seperti Boost.Tokenizer ) menawarkan tokeniser tertentu.
Pemecahan yang lebih maju membutuhkan ekspresi reguler. C ++ menyediakan std::regex_token_iteratoruntuk tujuan ini khususnya:
autoconst str ="The quick brown fox"s;autoconst re = std::regex{R"(\s+)"};autoconst vec = std::vector<std::string>(
std::sregex_token_iterator{begin(str), end(str), re,-1},
std::sregex_token_iterator{});
Sayangnya, peningkatan tidak selalu tersedia untuk semua proyek. Saya harus mencari jawaban yang tidak mendukung.
FuzzyBunnySlippers
36
Tidak setiap proyek terbuka untuk "open source". Saya bekerja di industri yang sangat diatur. Itu bukan masalah, sungguh. Itu hanya fakta kehidupan. Peningkatan tidak tersedia di mana-mana.
FuzzyBunnySlippers
5
@NonlinearIdeas Pertanyaan / jawaban lain sama sekali bukan tentang proyek Open Source. Hal yang sama berlaku untuk proyek apa pun . Yang mengatakan, saya tentu saja mengerti tentang standar terbatas seperti MISRA C tetapi kemudian dipahami bahwa Anda membangun semuanya dari awal (kecuali jika Anda kebetulan menemukan perpustakaan yang sesuai - jarang). Pokoknya, intinya adalah bahwa "Peningkatan tidak tersedia" - itu adalah bahwa Anda memiliki persyaratan khusus yang hampir semua jawaban untuk tujuan umum tidak cocok.
Konrad Rudolph
1
@NonlinearIdeas Contohnya, yang lain, jawaban non-Boost juga tidak sesuai dengan MISRA.
Konrad Rudolph
3
@Dmitry Apa “STL barf” ?! Dan seluruh komunitas sangat setuju untuk mengganti preprosesor C - pada kenyataannya, ada proposal untuk melakukan itu. Tetapi saran Anda untuk menggunakan PHP atau bahasa lain sebagai gantinya akan menjadi langkah besar mundur.
Konrad Rudolph
188
Kelas tokenizer Boost dapat membuat hal semacam ini cukup sederhana:
#include<iostream>#include<string>#include<boost/foreach.hpp>#include<boost/tokenizer.hpp>usingnamespace std;usingnamespace boost;int main(int,char**){
string text ="token, test string";
char_separator<char> sep(", ");
tokenizer< char_separator<char>> tokens(text, sep);
BOOST_FOREACH (const string& t, tokens){
cout << t <<"."<< endl;}}
Diperbarui untuk C ++ 11:
#include<iostream>#include<string>#include<boost/tokenizer.hpp>usingnamespace std;usingnamespace boost;int main(int,char**){
string text ="token, test string";
char_separator<char> sep(", ");
tokenizer<char_separator<char>> tokens(text, sep);for(constauto& t : tokens){
cout << t <<"."<< endl;}}
Bagus, saya baru saja memanfaatkan ini. Kompiler Visual Studio saya memiliki deringan aneh sampai saya menggunakan spasi putih untuk memisahkan dua ">" karakter sebelum bit token (teks, sep): (kesalahan C2947: mengharapkan '>' untuk mengakhiri templat-argumen-daftar, ditemukan '> > ')
AndyUK
@AndyUK ya, tanpa ruang kompiler mem-parsingnya sebagai operator ekstraksi daripada dua templat penutup.
EnabrenTane
Secara teoritis itu sudah diperbaiki di C ++ 0x
David Souther
3
Waspadalah terhadap parameter ketiga char_separatorkonstruktor ( drop_empty_tokensadalah default, alternatifnya adalah keep_empty_tokens).
Benoit
5
@puk - Ini adalah akhiran yang umum digunakan untuk file header C ++. (seperti .huntuk header C)
Ferruccio
167
Inilah yang sangat sederhana:
#include<vector>#include<string>usingnamespace std;vector<string> split(constchar*str,char c =' '){vector<string> result;do{constchar*begin = str;while(*str != c &&*str)
str++;
result.push_back(string(begin, str));}while(0!=*str++);return result;}
saya perlu menambahkan prototipe untuk metode ini dalam file .h?
Suhrob Samiev
5
Ini bukan jawaban "terbaik" karena masih menggunakan string literal yang merupakan array karakter konstan C polos. Saya percaya penanya bertanya apakah dia bisa tokenize string C ++ yang merupakan tipe "string" yang diperkenalkan oleh yang terakhir.
Vijay Kumar Kanta
Ini membutuhkan jawaban baru karena saya sangat curiga penyertaan ekspresi reguler dalam C ++ 11 telah mengubah apa jawaban terbaiknya.
Mahakuasa
113
Gunakan strtok. Menurut pendapat saya, tidak ada kebutuhan untuk membangun kelas di sekitar tokenizing kecuali strtok tidak memberi Anda apa yang Anda butuhkan. Mungkin tidak, tetapi dalam 15+ tahun menulis berbagai kode parsing dalam C dan C ++, saya selalu menggunakan strtok. Berikut ini sebuah contoh
char myString[]="The quick brown fox";char*p = strtok(myString," ");while(p){
printf ("Token: %s\n", p);
p = strtok(NULL," ");}
Beberapa peringatan (yang mungkin tidak sesuai dengan kebutuhan Anda). String "dihancurkan" dalam proses, yang berarti bahwa karakter EOS ditempatkan sejajar di tempat pembatas. Penggunaan yang benar mungkin mengharuskan Anda membuat versi string non-const. Anda juga dapat mengubah daftar pembatas mid parse.
Menurut pendapat saya sendiri, kode di atas jauh lebih sederhana dan lebih mudah digunakan daripada menulis kelas terpisah untuk itu. Bagi saya, ini adalah salah satu fungsi yang disediakan oleh bahasa dan berfungsi dengan baik dan bersih. Ini hanyalah solusi "berbasis C". Sangat tepat, mudah, dan Anda tidak perlu menulis banyak kode tambahan :-)
Bukannya saya tidak suka C, tetapi strtok tidak aman untuk thread, dan Anda harus yakin bahwa string yang Anda kirim mengandung karakter nol untuk menghindari kemungkinan buffer overflow.
menyapa
11
Ada strtok_r, tapi ini pertanyaan C ++.
Kontrak Prof. Falken dilanggar
3
@tloach: dalam strtok kompiler MS C ++ adalah thread aman karena variabel statis internal dibuat pada TLS (penyimpanan lokal thread) (sebenarnya itu tergantung pada kompiler)
Ahmed Said
3
@ ahmed: thread aman berarti lebih dari sekadar dapat menjalankan fungsi dua kali dalam utas yang berbeda. Dalam hal ini jika utas dimodifikasi saat strtok berjalan, mungkin saja string tersebut valid selama seluruh proses strtok, tetapi strtok masih akan berantakan karena string berubah, sekarang sudah melewati karakter nol, dan itu akan terus membaca memori sampai terjadi pelanggaran keamanan atau menemukan karakter nol. Ini adalah masalah dengan fungsi string C asli, jika Anda tidak menentukan panjang di suatu tempat Anda mengalami masalah.
tloach
4
strtok memerlukan pointer ke array char non-const null-dihentikan, yang bukan makhluk umum untuk menemukan dalam kode c ++ ... apa cara favorit Anda untuk mengkonversi ini dari std :: string?
fuzzyTew
105
Cara cepat lainnya adalah menggunakan getline. Sesuatu seperti:
Saya mengalami masalah dalam menggunakan teknik ini dengan karakter 0x0A dalam string yang membuat loop sementara keluar sebelum waktunya. Kalau tidak, ini adalah solusi sederhana dan cepat yang bagus.
Ryan H.
4
Ini bagus tetapi hanya perlu diingat bahwa dengan melakukan ini pembatas default '\ n' tidak dipertimbangkan. Contoh ini akan berfungsi, tetapi jika Anda menggunakan sesuatu seperti: while (getline (inFile, word, '')) di mana inFile adalah objek ifstream yang berisi beberapa baris, Anda akan mendapatkan hasil yang
lucu
terlalu buruk getline mengembalikan stream daripada string, membuatnya tidak dapat digunakan dalam daftar inisialisasi tanpa penyimpanan sementara
fuzzyTew
1
Keren! Tanpa peningkatan dan C ++ 11, solusi yang bagus untuk proyek-proyek warisan di luar sana!
Deqing
1
ITULAH jawabannya, nama fungsinya agak canggung.
Nils
82
Anda dapat menggunakan stream, iterators, dan algoritma salin untuk melakukan ini secara langsung.
#include<string>#include<vector>#include<iostream>#include<istream>#include<ostream>#include<iterator>#include<sstream>#include<algorithm>int main(){
std::string str ="The quick brown fox";// construct a stream from the string
std::stringstream strstr(str);// use stream iterators to copy the stream to the vector as whitespace separated strings
std::istream_iterator<std::string> it(strstr);
std::istream_iterator<std::string> end;
std::vector<std::string> results(it, end);// send the vector to stdout.
std::ostream_iterator<std::string> oit(std::cout);
std::copy(results.begin(), results.end(), oit);}
Saya menemukan std :: menjengkelkan untuk membaca .. mengapa tidak menggunakan "menggunakan"?
user35978
80
@Vadi: karena mengedit posting orang lain cukup mengganggu. @pheze: Saya lebih suka membiarkan stdcara ini saya tahu dari mana objek saya berasal, itu hanya masalah gaya.
Matthieu M.
7
Saya mengerti alasan Anda dan saya pikir itu sebenarnya pilihan yang baik jika itu cocok untuk Anda, tetapi dari sudut pandang pedagogis saya sebenarnya setuju dengan pheze. Lebih mudah untuk membaca dan memahami contoh yang benar-benar asing seperti ini dengan "menggunakan namespace std" di bagian atas karena memerlukan sedikit usaha untuk menafsirkan baris berikut ... terutama dalam kasus ini karena semuanya dari perpustakaan standar. Anda dapat membuatnya mudah dibaca dan jelas dari mana objek berasal dengan serangkaian "using std :: string;" dll. Terutama karena fungsinya sangat singkat.
cheshirekow
61
Meskipun awalan "std ::" menjadi menjengkelkan atau jelek, yang terbaik adalah memasukkannya dalam kode contoh sehingga sangat jelas dari mana fungsi-fungsi ini berasal. Jika mereka mengganggu Anda, mudah untuk menggantinya dengan "menggunakan" setelah Anda mencuri contoh dan mengklaimnya sebagai milik Anda.
dlchambers
20
ya! apa yang dia katakan! praktik terbaik adalah dengan menggunakan awalan std. Basis kode besar tidak diragukan lagi akan memiliki perpustakaan dan ruang nama itu sendiri dan menggunakan "using namespace std" akan membuat Anda sakit kepala ketika Anda mulai menyebabkan konflik namespace.
Miek
48
Tidak ada orang tersinggung, tapi untuk suatu masalah sederhana, Anda membuat hal-hal cara terlalu rumit. Ada banyak alasan untuk menggunakan Peningkatan . Tapi untuk sesuatu yang sederhana ini, rasanya seperti memukul lalat dengan sledge 20 #.
void
split(vector<string>& theStringVector,/* Altered/returned value */const string & theString,const string & theDelimiter){
UASSERT( theDelimiter.size(),>,0);// My own ASSERT macro.size_t start =0, end =0;while( end != string::npos){
end = theString.find( theDelimiter, start);// If at end, use length=maxLength. Else use length=end-start.
theStringVector.push_back( theString.substr( start,(end == string::npos)? string::npos : end - start));// If at end, use start=maxSize. Else use start=end+delimiter.
start =(( end >(string::npos - theDelimiter.size()))? string::npos : end + theDelimiter.size());}}
Misalnya (untuk kasus Doug),
#define SHOW(I,X) cout <<"["<<(I)<<"]\t "# X " = \"" << (X) << "\"" << endlint
main(){vector<string> v;
split( v,"A:PEP:909:Inventory Item",":");for(unsignedint i =0; i < v.size(); i++)
SHOW( i, v[i]);}
Dan ya, kita bisa membagi () mengembalikan vektor baru daripada melewati satu. Ini sepele untuk membungkus dan membebani. Tetapi tergantung pada apa yang saya lakukan, saya sering merasa lebih baik untuk menggunakan kembali objek yang sudah ada daripada selalu membuat yang baru. (Asalkan saya tidak lupa mengosongkan vektor di antaranya!)
Mengapa mendefinisikan makro yang hanya Anda gunakan di satu tempat. Dan bagaimana UASSERT Anda lebih baik daripada pernyataan standar. Memisahkan perbandingan menjadi 3 token seperti itu tidak membutuhkan apa pun selain membutuhkan lebih banyak koma daripada yang seharusnya Anda perlukan.
crelbor
1
Mungkin makro UASSERT menunjukkan (dalam pesan kesalahan) hubungan aktual antara (dan nilai-nilai) dua nilai yang dibandingkan? Itu sebenarnya ide yang cukup bagus, IMHO.
GhassanPL
10
Ugh, mengapa std::stringkelas tidak menyertakan fungsi split ()?
Tn. Shickadance
Saya pikir baris terakhir dalam while loop seharusnya start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());dan while loop seharusnya while (start != string::npos). Saya juga memeriksa substring untuk memastikan tidak kosong sebelum memasukkannya ke vektor.
John K
@ JohnK Jika input memiliki dua pembatas berurutan, maka jelas string di antara keduanya kosong, dan harus dimasukkan ke dalam vektor. Jika nilai kosong tidak dapat diterima untuk tujuan tertentu, itu adalah hal lain, tetapi IMHO batasan seperti itu harus ditegakkan di luar jenis fungsi tujuan yang sangat umum.
Lauri Nurmi
46
Solusi menggunakan regex_token_iterators:
#include<iostream>#include<regex>#include<string>usingnamespace std;int main(){
string str("The quick brown fox");
regex reg("\\s+");
sregex_token_iterator iter(str.begin(), str.end(), reg,-1);
sregex_token_iterator end;vector<string> vec(iter, end);for(auto a : vec){
cout << a << endl;}}
Ini harus menjadi jawaban peringkat teratas. Ini adalah cara yang tepat untuk melakukan ini dalam C ++> = 11.
Mahakuasa
1
Saya senang saya telah menggulir ke bawah ke jawaban ini (saat ini hanya memiliki 9 upvotes). Ini persis seperti apa kode C ++ 11 seharusnya terlihat untuk tugas ini!
YePhIcK
Jawaban luar biasa yang tidak bergantung pada perpustakaan eksternal dan menggunakan perpustakaan yang sudah tersedia
Andrew
1
Jawaban yang bagus, memberi fleksibilitas paling banyak pada pembatas. Beberapa peringatan: Menggunakan \ s + regex menghindari token kosong di tengah teks, tetapi memberikan token pertama yang kosong jika teks dimulai dengan spasi putih. Juga, regex tampaknya lambat: pada laptop saya, untuk 20 MB teks acak, dibutuhkan 0,6 detik, dibandingkan dengan 0,014 detik untuk strtok, strsep, atau jawaban Parham menggunakan str.find_first_of, atau 0,027 detik untuk Perl, atau 0,021 detik untuk Python . Untuk teks pendek, kecepatan mungkin tidak menjadi masalah.
Mark Gates
2
Oke, mungkin terlihat keren, tapi ini jelas terlalu sering menggunakan ekspresi reguler. Wajar hanya jika Anda tidak peduli dengan kinerja.
#include<vector>#include<boost/algorithm/string.hpp>int main(){auto s ="a,b, c ,,e,f,";
std::vector<std::string> fields;
boost::split(fields, s, boost::is_any_of(","));for(constauto& field : fields)
std::cout <<"\""<< field <<"\"\n";return0;}
Ini adalah solusi STL-only sederhana (~ 5 baris!) Menggunakan std::finddan std::find_first_not_ofyang menangani pengulangan pembatas (seperti spasi atau periode misalnya), serta pembatas memimpin dan mengikuti:
#include<string>#include<vector>void tokenize(std::string str, std::vector<string>&token_v){size_t start = str.find_first_not_of(DELIMITER), end=start;while(start != std::string::npos){// Find next occurence of delimiter
end = str.find(DELIMITER, start);// Push back the token found into vector
token_v.push_back(str.substr(start, end-start));// Skip all occurences of the delimiter to find new start
start = str.find_first_not_of(DELIMITER, end);}}
Ini bagus, tapi saya pikir Anda perlu menggunakan find_first_of () alih-alih find () agar ini berfungsi dengan baik dengan beberapa pembatas.
2
@ user755921 beberapa pembatas dilewati saat menemukan posisi awal dengan find_first_not_of.
Pemula
16
pystring adalah perpustakaan kecil yang mengimplementasikan banyak fungsi string Python, termasuk metode split:
#include<string>#include<vector>#include"pystring.h"
std::vector<std::string> chunks;
pystring::split("this string", chunks);// also can specify a separator
pystring::split("this-string", chunks,"-");
Wow, Anda telah menjawab pertanyaan langsung saya dan banyak pertanyaan di masa depan. Saya mendapatkan bahwa c ++ sangat kuat. Tetapi ketika memisahkan string menghasilkan kode sumber seperti jawaban di atas, itu jelas mengecewakan. Saya ingin tahu perpustakaan lain seperti ini yang menurunkan kenyamanan bahasa tingkat tinggi.
Ross
wow, kamu benar-benar baru saja membuat hariku !! tidak tahu tentang pystring. ini akan menghemat banyak waktu!
accraze
11
Saya memposting jawaban ini untuk pertanyaan serupa.
Jangan menemukan kembali roda. Saya telah menggunakan sejumlah perpustakaan dan yang tercepat dan paling fleksibel yang pernah saya temui adalah: C ++ String Toolkit Library .
Berikut adalah contoh cara menggunakannya yang saya posting di tempat lain di stackoverflow.
#include<iostream>#include<vector>#include<string>#include<strtk.hpp>constchar*whitespace =" \t\r\n\f";constchar*whitespace_and_punctuation =" \t\r\n\f;,=";int main(){{// normal parsing of a string into a vector of strings
std::string s("Somewhere down the road");
std::vector<std::string> result;if( strtk::parse( s, whitespace, result )){for(size_t i =0; i < result.size();++i )
std::cout << result[i]<< std::endl;}}{// parsing a string into a vector of floats with other separators// besides spaces
std::string s("3.0, 3.14; 4.0");
std::vector<float> values;if( strtk::parse( s, whitespace_and_punctuation, values )){for(size_t i =0; i < values.size();++i )
std::cout << values[i]<< std::endl;}}{// parsing a string into specific variables
std::string s("angle = 45; radius = 9.9");
std::string w1, w2;float v1, v2;if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2)){
std::cout <<"word "<< w1 <<", value "<< v1 << std::endl;
std::cout <<"word "<< w2 <<", value "<< v2 << std::endl;}}return0;}
#include<iostream>#include<sstream>usingnamespace std;int main (){
string tmps;
istringstream is ("the dellimiter is the space");while(is.good ()){
is >> tmps;
cout << tmps <<"\n";}return0;}
Fungsi Tokenize () ini akan melewati token kosong, misalnya, jika ada substring "%%" di string utama, tidak ada token kosong yang dikembalikan. Itu dilewati.
Sheen
4
Jika Anda ingin menggunakan C, Anda dapat menggunakan fungsi strtok . Anda harus memperhatikan masalah multi-threading saat menggunakannya.
Perhatikan bahwa strtok memodifikasi string yang Anda periksa, sehingga Anda tidak dapat menggunakannya pada string const * tanpa membuat salinan.
Graeme Perrow
9
Masalah multithreading adalah bahwa strtok menggunakan variabel global untuk melacak di mana itu, jadi jika Anda memiliki dua utas yang masing-masing menggunakan strtok, Anda akan mendapatkan perilaku yang tidak terdefinisi.
JohnMcG
@JohnMcG Atau gunakan saja strtok_syang pada dasarnya strtokdengan status state passing
Matthias
4
Untuk hal-hal sederhana, saya hanya menggunakan yang berikut ini:
Sangkalan pengecut: Saya menulis perangkat lunak pengolah data waktu-nyata di mana data masuk melalui file biner, soket, atau panggilan API (kartu I / O, kamera). Saya tidak pernah menggunakan fungsi ini untuk sesuatu yang lebih rumit atau kritis waktu daripada membaca file konfigurasi eksternal saat startup.
+1 untuk menyarankan regex, jika Anda tidak memerlukan kecepatan warp itu adalah solusi yang paling fleksibel, belum didukung di mana-mana tetapi seiring berjalannya waktu itu akan menjadi kurang penting.
odinthenerd
+1 dari saya, baru saja mencoba <regex> di c ++ 11. Sangat sederhana dan elegan
StahlRat
4
Banyak saran yang terlalu rumit di sini. Coba std :: string solution sederhana ini:
Kesalahan saya karena memberi contoh yang buruk (terlalu sederhana). Sejauh yang saya tahu, itu hanya berfungsi ketika pembatas Anda adalah spasi putih.
Bill the Lizard
4
Jawaban Adam Pierce menyediakan tokenizer pintal tangan dengan a const char*. Ini sedikit lebih bermasalah untuk dilakukan dengan iterator karena penambahan stringiterator akhir tidak terdefinisi . Yang mengatakan, mengingat string str{ "The quick brown fox" }kita pasti bisa mencapai ini:
Jika Anda mencari kompleksitas abstrak dengan menggunakan fungsionalitas standar, seperti yang disarankan On Freundstrtok adalah opsi sederhana:
vector<string> tokens;for(auto i = strtok(data(str)," "); i !=nullptr; i = strtok(nullptr," ")) tokens.push_back(i);
Jika Anda tidak memiliki akses ke C ++ 17, Anda harus mengganti data(str)seperti pada contoh ini: http://ideone.com/8kAGoa
Meskipun tidak diperlihatkan dalam contoh, strtoktidak perlu menggunakan pembatas yang sama untuk setiap token. Seiring dengan keunggulan ini, ada beberapa kelemahan:
strtoktidak dapat digunakan pada multipel stringspada waktu yang bersamaan: Entah nullptrharus dilewati untuk melanjutkan tokenizing the currentstring atau yang baru char*untuk tokenize harus dilewati (ada beberapa implementasi non-standar yang mendukung ini, seperti:strtok_s )
Untuk alasan yang sama strtok tidak dapat digunakan pada banyak utas secara bersamaan (namun ini mungkin implementasi yang ditentukan, misalnya: Implementasi Visual Studio adalah utas yang aman )
Memanggil strtokmengubah stringitu beroperasi, sehingga tidak dapat digunakan pada const strings, const char*s, atau string literal, untuk tokenize semua ini dengan strtokatau untuk beroperasi padastring siapa yang perlu dilestarikan konten, strharus disalin, maka salinan dapat Dioperasikan
Metode sebelumnya tidak dapat menghasilkan tokenized vectordi tempat, artinya tanpa mengabstraksikannya menjadi fungsi pembantu yang tidak dapat diinisialisasi const vector<string> tokens. Fungsionalitas itu dan kemampuan untuk menerima pembatas ruang putih apa pun dapat dimanfaatkan menggunakan istream_iterator. Misalnya diberikan: const string str{ "The quick \tbrown \nfox" }kita bisa melakukan ini:
Diperlukan pembangunan sebuah istringstream untuk opsi ini memiliki biaya yang jauh lebih besar daripada 2 opsi sebelumnya, namun biaya ini biasanya tersembunyi dalam biaya stringalokasi.
Jika tidak ada opsi di atas yang cukup fleksibel untuk kebutuhan tokenization Anda, opsi yang paling fleksibel adalah dengan menggunakan regex_token_iteratortentu saja dengan fleksibilitas ini muncul biaya yang lebih besar, tetapi sekali lagi ini kemungkinan tersembunyi dalam stringbiaya alokasi. Katakan misalnya kita ingin tokenize berdasarkan koma yang tidak diloloskan, juga memakan ruang putih, diberi masukan berikut: const string str{ "The ,qu\\,ick ,\tbrown, fox" }kita bisa melakukan ini:
strtok_sadalah standar C11, omong-omong. strtok_radalah standar POSIX2001. Di antara keduanya, ada versi standar strtokuntuk sebagian besar platform.
Andon M. Coleman
@ AndonM.Coleman Tapi ini adalah pertanyaan c ++ , dan di C ++ #include <cstring>hanya menyertakan versi c99strtok . Jadi asumsi saya adalah Anda hanya memberikan komentar ini sebagai bahan pendukung, menunjukkan penerapan strtokekstensi yang spesifik ?
Jonathan Mee
1
Hanya saja itu tidak non-standar seperti yang mungkin dipercaya orang. strtok_sdisediakan oleh C11 dan sebagai ekstensi mandiri dalam runtime C Microsoft. Ada sedikit sejarah yang aneh di sini di mana _sfungsi Microsoft menjadi standar C.
Andon M. Coleman
@ AndonM.Coleman Benar, aku bersamamu. Tentunya jika itu ada dalam standar C11 antarmuka dan implementasi memiliki batasan yang ditempatkan pada mereka yang membutuhkan perilaku yang identik independen dari platform. Sekarang satu-satunya masalah adalah memastikan bahwa fungsi C11 tersedia untuk kita di seluruh platform. Mudah-mudahan standar C11 akan menjadi sesuatu yang dipilih C ++ 17 atau C ++ 20 untuk diambil.
Jonathan Mee
3
Saya tahu pertanyaan ini sudah dijawab tetapi saya ingin berkontribusi. Mungkin solusi saya agak sederhana tetapi inilah yang saya buat:
Tampak aneh bagi saya bahwa dengan kita semua kutu buku sadar kecepatan di sini pada SO tidak ada yang menyajikan versi yang menggunakan waktu kompilasi yang dihasilkan mencari tabel untuk pembatas (contoh implementasi lebih jauh ke bawah). Menggunakan tabel pencarian dan iterator harus mengalahkan std :: regex dalam efisiensi, jika Anda tidak perlu mengalahkan regex, cukup gunakan, standarnya seperti pada C ++ 11 dan super fleksibel.
Beberapa sudah menyarankan regex tetapi untuk noobs di sini adalah contoh paket yang harus melakukan persis apa yang diharapkan OP:
std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
std::smatch m{};
std::vector<std::string> ret{};while(std::regex_search (it,end,m,e)){
ret.emplace_back(m.str());
std::advance(it, m.position()+ m.length());//next start position = match position + match length}return ret;}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){//comfort version calls flexible versionreturn split(s.cbegin(), s.cend(), std::move(e));}int main (){
std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};auto v = split(str);for(constauto&s:v){
std::cout << s << std::endl;}
std::cout <<"crazy version:"<< std::endl;
v = split(str, std::regex{"[^e]+"});//using e as delim shows flexibilityfor(constauto&s:v){
std::cout << s << std::endl;}return0;}
Jika kita perlu lebih cepat dan menerima batasan bahwa semua karakter harus 8 bit, kita bisa membuat tabel pencarian pada waktu kompilasi menggunakan metaprogramming:
template<bool...>structBoolSequence{};//just here to hold boolstemplate<char...>structCharSequence{};//just here to hold charstemplate<typename T,char C>structContains;//generictemplate<charFirst,char...Cs,charMatch>//not first specializationstructContains<CharSequence<First,Cs...>,Match>:Contains<CharSequence<Cs...>,Match>{};//strip first and increase indextemplate<charFirst,char...Cs>//is first specializationstructContains<CharSequence<First,Cs...>,First>: std::true_type {};template<charMatch>//not found specializationstructContains<CharSequence<>,Match>: std::false_type{};template<int I,typename T,typename U>structMakeSequence;//generictemplate<int I,bool...Bs,typename U>structMakeSequence<I,BoolSequence<Bs...>, U>://not lastMakeSequence<I-1,BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};template<bool...Bs,typename U>structMakeSequence<0,BoolSequence<Bs...>,U>{//last usingType=BoolSequence<Bs...>;};template<typename T>structBoolASCIITable;template<bool...Bs>structBoolASCIITable<BoolSequence<Bs...>>{/* could be made constexpr but not yet supported by MSVC */staticbool isDelim(constchar c){staticconstbool table[256]={Bs...};return table[static_cast<int>(c)];}};usingDelims=CharSequence<'.',',',' ',':','\n'>;//list your custom delimiters hereusingTable=BoolASCIITable<typenameMakeSequence<256,BoolSequence<>,Delims>::Type>;
Dengan itu, membuat getNextTokenfungsi menjadi mudah:
template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
begin = std::find_if(begin,end,std::not1(Table{}));//find first non delim or endauto second = std::find_if(begin,end,Table{});//find first delim or endreturn std::make_pair(begin,second);}
Menggunakannya juga mudah:
int main(){
std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};auto it = std::begin(s);auto end = std::end(s);while(it != std::end(s)){auto token = getNextToken(it,end);
std::cout << std::string(token.first,token.second)<< std::endl;
it = token.second;}return0;}
Apakah mungkin untuk melakukan tokennize dengan pembatas String?
Galigator
versi ini hanya dioptimalkan untuk pembatas karakter tunggal, menggunakan tabel look up tidak cocok untuk pembatas multi-karakter (string) sehingga lebih sulit untuk mengalahkan regex dalam efisiensi.
odinthenerd
1
Anda dapat memanfaatkan boost :: make_find_iterator. Sesuatu yang mirip dengan ini:
template<typename CH>inlinevector< basic_string<CH>> tokenize(const basic_string<CH>&Input,const basic_string<CH>&Delimiter,bool remove_empty_token
){typedeftypename basic_string<CH>::const_iteratorstring_iterator_t;typedef boost::find_iterator<string_iterator_t>string_find_iterator_t;vector< basic_string<CH>>Result;string_iterator_t it =Input.begin();string_iterator_t it_end =Input.end();for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
i !=string_find_iterator_t();++i){if(remove_empty_token){if(it != i->begin())Result.push_back(basic_string<CH>(it,i->begin()));}elseResult.push_back(basic_string<CH>(it,i->begin()));
it = i->end();}if(it != it_end)Result.push_back(basic_string<CH>(it,it_end));returnResult;}
Inilah Swiss-Army Knife saya yang menggunakan tokenizer string untuk memisahkan string berdasarkan spasi, menghitung string yang dibungkus dengan tanda kutip tunggal dan ganda serta menghilangkan karakter-karakter tersebut dari hasil. Saya menggunakan RegexBuddy 4.x untuk menghasilkan sebagian besar snipet kode, tetapi saya menambahkan penanganan khusus untuk pengupasan kutipan dan beberapa hal lainnya.
Suara (Down) dapat sama konstruktifnya dengan upvotes, tetapi tidak ketika Anda tidak memberikan komentar mengapa ...
kayleeFrye_onDeck
1
Saya menyamakan Anda tetapi mungkin karena kode itu terlihat cukup menakutkan bagi programmer googling 'cara membagi string' terutama tanpa dokumentasi
mattshu
Terima kasih @mattshu! Apakah segmen regex yang membuatnya menakutkan atau sesuatu yang lain?
kayleeFrye_onDeck
0
Jika panjang maksimum dari string input yang akan dipatuhi diketahui, seseorang dapat mengeksploitasi ini dan mengimplementasikan versi yang sangat cepat. Saya membuat sketsa ide dasar di bawah ini, yang terinspirasi oleh strtok () dan struktur "suffix array" -data yang dijelaskan Jon Bentley "Programming Perls" edisi ke-2, bab 15. Kelas C ++ dalam hal ini hanya memberikan beberapa organisasi dan kenyamanan penggunaan. Implementasi yang ditunjukkan dapat dengan mudah diperluas untuk menghapus karakter spasi putih terkemuka dan tertinggal di dalam token.
Pada dasarnya seseorang dapat mengganti karakter pemisah dengan karakter penghentian string '\ 0' dan mengatur pointer ke token dengan string yang dimodifikasi. Dalam kasus ekstrim ketika string hanya terdiri dari pemisah, satu mendapat panjang string ditambah 1 yang dihasilkan token kosong. Praktis untuk menduplikasi string yang akan dimodifikasi.
File tajuk:
classTextLineSplitter{public:TextLineSplitter(constsize_t max_line_len );~TextLineSplitter();voidSplitLine(constchar*line,constchar sep_char =',',);inlinesize_tNumTokens(void)const{return mNumTokens;}constchar*GetToken(constsize_t token_idx )const{
assert( token_idx < mNumTokens );return mTokens[ token_idx ];}private:constsize_t mStorageSize;char*mBuff;char**mTokens;size_t mNumTokens;inlinevoidResetContent(void){
memset( mBuff,0, mStorageSize );// mark all items as empty:
memset( mTokens,0, mStorageSize *sizeof(char*));// reset counter for found items:
mNumTokens =0L;}};
// create an instance capable of splitting strings up to 1000 chars long:TextLineSplitter spl(1000);
spl.SplitLine("Item1,,Item2,Item3");for(size_t i =0; i < spl.NumTokens(); i++){
printf("%s\n", spl.GetToken( i ));}
boost::tokenizeradalah teman Anda, tetapi pertimbangkan untuk menjadikan kode Anda portabel dengan mengacu pada masalah internasionalisasi (i18n) dengan menggunakan wstring/ wchar_tbukan warisan string/ chartipe.
#include<iostream>#include<boost/tokenizer.hpp>#include<string>usingnamespace std;usingnamespace boost;typedef tokenizer<char_separator<wchar_t>,
wstring::const_iterator, wstring>Tok;int main(){
wstring s;while(getline(wcin, s)){
char_separator<wchar_t> sep(L" ");// list of separator charactersTok tok(s, sep);for(Tok::iterator beg = tok.begin(); beg != tok.end();++beg){
wcout <<*beg << L"\t";// output (or store in vector)}
wcout << L"\n";}return0;}
"Warisan" jelas tidak benar dan wchar_tmerupakan tipe ketergantungan implementasi yang mengerikan yang tidak boleh digunakan siapa pun kecuali benar-benar diperlukan.
CoffeeandCode
Penggunaan wchar_t entah bagaimana tidak secara otomatis menyelesaikan masalah i18n. Anda menggunakan penyandian untuk menyelesaikan masalah itu. Jika Anda memisahkan string dengan pembatas, tersirat bahwa pembatas tidak bertabrakan dengan konten yang disandikan dari setiap token di dalam string. Lolos mungkin diperlukan, dll. Wchar_t bukan solusi ajaib untuk ini.
yonil
0
Kode C ++ sederhana (standar C ++ 98), menerima banyak pembatas (ditentukan dalam std :: string), hanya menggunakan vektor, string, dan iterator.
#include<iostream>#include<vector>#include<string>#include<stdexcept>
std::vector<std::string>
split(const std::string& str,const std::string& delim){
std::vector<std::string> result;if(str.empty())throw std::runtime_error("Can not tokenize an empty string!");
std::string::const_iterator begin, str_it;
begin = str_it = str.begin();do{while(delim.find(*str_it)== std::string::npos && str_it != str.end())
str_it++;// find the position of the first delimiter in str
std::string token = std::string(begin, str_it);// grab the tokenif(!token.empty())// empty token only when str starts with a delimiter
result.push_back(token);// push the token into a vector<string>while(delim.find(*str_it)!= std::string::npos && str_it != str.end())
str_it++;// ignore the additional consecutive delimiters
begin = str_it;// process the remaining tokens}while(str_it != str.end());return result;}int main(){
std::string test_string =".this is.a.../.simple;;test;;;END";
std::string delim ="; ./";// string containing the delimiters
std::vector<std::string> tokens = split(test_string, delim);for(std::vector<std::string>::const_iterator it = tokens.begin();
it != tokens.end(); it++)
std::cout <<*it << std::endl;}
Jawaban:
Algoritma perpustakaan standar C ++ cukup universal berbasis di sekitar iterator daripada wadah beton. Sayangnya ini membuatnya sulit untuk menyediakan
split
fungsi mirip Java di pustaka standar C ++, meskipun tidak ada yang berpendapat bahwa ini akan lebih mudah. Tapi seperti apa tipe pengembaliannya?std::vector<std::basic_string<…>>
? Mungkin, tapi kemudian kami terpaksa melakukan alokasi (berpotensi berlebihan dan mahal).Alih-alih, C ++ menawarkan sejumlah besar cara untuk membagi string berdasarkan pembatas yang kompleks secara arbitrer, tetapi tidak satupun dari mereka yang dienkapsulasi sebaik di bahasa lain. Banyak cara mengisi seluruh posting blog .
Paling sederhana, Anda bisa beralih menggunakan
std::string::find
sampai Anda menekanstd::string::npos
, dan ekstrak konten menggunakanstd::string::substr
.Versi yang lebih lancar (dan idiomatik, tetapi mendasar) untuk membelah di whitespace akan menggunakan
std::istringstream
:Menggunakan
std::istream_iterator
s , isi dari stream string juga dapat disalin ke dalam vektor menggunakan konstruktor rentang iteratornya.Beberapa perpustakaan (seperti Boost.Tokenizer ) menawarkan tokeniser tertentu.
Pemecahan yang lebih maju membutuhkan ekspresi reguler. C ++ menyediakan
std::regex_token_iterator
untuk tujuan ini khususnya:sumber
Kelas tokenizer Boost dapat membuat hal semacam ini cukup sederhana:
Diperbarui untuk C ++ 11:
sumber
char_separator
konstruktor (drop_empty_tokens
adalah default, alternatifnya adalahkeep_empty_tokens
)..h
untuk header C)Inilah yang sangat sederhana:
sumber
Gunakan strtok. Menurut pendapat saya, tidak ada kebutuhan untuk membangun kelas di sekitar tokenizing kecuali strtok tidak memberi Anda apa yang Anda butuhkan. Mungkin tidak, tetapi dalam 15+ tahun menulis berbagai kode parsing dalam C dan C ++, saya selalu menggunakan strtok. Berikut ini sebuah contoh
Beberapa peringatan (yang mungkin tidak sesuai dengan kebutuhan Anda). String "dihancurkan" dalam proses, yang berarti bahwa karakter EOS ditempatkan sejajar di tempat pembatas. Penggunaan yang benar mungkin mengharuskan Anda membuat versi string non-const. Anda juga dapat mengubah daftar pembatas mid parse.
Menurut pendapat saya sendiri, kode di atas jauh lebih sederhana dan lebih mudah digunakan daripada menulis kelas terpisah untuk itu. Bagi saya, ini adalah salah satu fungsi yang disediakan oleh bahasa dan berfungsi dengan baik dan bersih. Ini hanyalah solusi "berbasis C". Sangat tepat, mudah, dan Anda tidak perlu menulis banyak kode tambahan :-)
sumber
Cara cepat lainnya adalah menggunakan
getline
. Sesuatu seperti:Jika mau, Anda dapat membuat
split()
metode sederhana untuk mengembalikanvector<string>
, yang sangat berguna.sumber
Anda dapat menggunakan stream, iterators, dan algoritma salin untuk melakukan ini secara langsung.
sumber
std
cara ini saya tahu dari mana objek saya berasal, itu hanya masalah gaya.Tidak ada orang tersinggung, tapi untuk suatu masalah sederhana, Anda membuat hal-hal cara terlalu rumit. Ada banyak alasan untuk menggunakan Peningkatan . Tapi untuk sesuatu yang sederhana ini, rasanya seperti memukul lalat dengan sledge 20 #.
Misalnya (untuk kasus Doug),
Dan ya, kita bisa membagi () mengembalikan vektor baru daripada melewati satu. Ini sepele untuk membungkus dan membebani. Tetapi tergantung pada apa yang saya lakukan, saya sering merasa lebih baik untuk menggunakan kembali objek yang sudah ada daripada selalu membuat yang baru. (Asalkan saya tidak lupa mengosongkan vektor di antaranya!)
Referensi: http://www.cplusplus.com/reference/string/string/ .
(Awalnya saya menulis jawaban untuk pertanyaan Doug: C ++ Strings Modifying and Extracting berdasarkan Separators (closed) . Tapi karena Martin York menutup pertanyaan itu dengan sebuah pointer di sini ... Saya hanya akan menggeneralisasi kode saya.)
sumber
std::string
kelas tidak menyertakan fungsi split ()?start = ((end > (theString.size() - theDelimiter.size())) ? string::npos : end + theDelimiter.size());
dan while loop seharusnyawhile (start != string::npos)
. Saya juga memeriksa substring untuk memastikan tidak kosong sebelum memasukkannya ke vektor.Solusi menggunakan
regex_token_iterator
s:sumber
Boost memiliki fungsi pemisahan yang kuat: boost :: algoritme :: split .
Program sampel:
Keluaran:
sumber
Saya tahu Anda meminta solusi C ++, tetapi Anda mungkin menganggap ini berguna:
Qt
Keuntungan dari Boost dalam contoh ini adalah pemetaan langsung ke kode pos Anda.
Lihat lebih lanjut di dokumentasi Qt
sumber
Berikut adalah contoh tokenizer kelas yang mungkin melakukan apa yang Anda inginkan
Contoh:
sumber
Ini adalah solusi STL-only sederhana (~ 5 baris!) Menggunakan
std::find
danstd::find_first_not_of
yang menangani pengulangan pembatas (seperti spasi atau periode misalnya), serta pembatas memimpin dan mengikuti:Cobalah langsung !
sumber
pystring adalah perpustakaan kecil yang mengimplementasikan banyak fungsi string Python, termasuk metode split:
sumber
Saya memposting jawaban ini untuk pertanyaan serupa.
Jangan menemukan kembali roda. Saya telah menggunakan sejumlah perpustakaan dan yang tercepat dan paling fleksibel yang pernah saya temui adalah: C ++ String Toolkit Library .
Berikut adalah contoh cara menggunakannya yang saya posting di tempat lain di stackoverflow.
sumber
Lihat contoh ini. Mungkin membantu Anda ..
sumber
while ( is >> tmps ) { std::cout << tmps << "\n"; }
MFC / ATL memiliki tokenizer yang sangat bagus. Dari MSDN:
sumber
Jika Anda ingin menggunakan C, Anda dapat menggunakan fungsi strtok . Anda harus memperhatikan masalah multi-threading saat menggunakannya.
sumber
strtok_s
yang pada dasarnyastrtok
dengan status state passingUntuk hal-hal sederhana, saya hanya menggunakan yang berikut ini:
Sangkalan pengecut: Saya menulis perangkat lunak pengolah data waktu-nyata di mana data masuk melalui file biner, soket, atau panggilan API (kartu I / O, kamera). Saya tidak pernah menggunakan fungsi ini untuk sesuatu yang lebih rumit atau kritis waktu daripada membaca file konfigurasi eksternal saat startup.
sumber
Anda cukup menggunakan pustaka ekspresi reguler dan mengatasinya dengan menggunakan ekspresi reguler.
Gunakan ekspresi (\ w +) dan variabel dalam \ 1 (atau $ 1 tergantung pada implementasi perpustakaan dari ekspresi reguler).
sumber
Banyak saran yang terlalu rumit di sini. Coba std :: string solution sederhana ini:
sumber
Saya pikir itulah gunanya
>>
operator pada string stream:sumber
Jawaban Adam Pierce menyediakan tokenizer pintal tangan dengan a
const char*
. Ini sedikit lebih bermasalah untuk dilakukan dengan iterator karena penambahanstring
iterator akhir tidak terdefinisi . Yang mengatakan, mengingatstring str{ "The quick brown fox" }
kita pasti bisa mencapai ini:Live Example
Jika Anda mencari kompleksitas abstrak dengan menggunakan fungsionalitas standar, seperti yang disarankan On Freund
strtok
adalah opsi sederhana:Jika Anda tidak memiliki akses ke C ++ 17, Anda harus mengganti
data(str)
seperti pada contoh ini: http://ideone.com/8kAGoaMeskipun tidak diperlihatkan dalam contoh,
strtok
tidak perlu menggunakan pembatas yang sama untuk setiap token. Seiring dengan keunggulan ini, ada beberapa kelemahan:strtok
tidak dapat digunakan pada multipelstrings
pada waktu yang bersamaan: Entahnullptr
harus dilewati untuk melanjutkan tokenizing the currentstring
atau yang baruchar*
untuk tokenize harus dilewati (ada beberapa implementasi non-standar yang mendukung ini, seperti:strtok_s
)strtok
tidak dapat digunakan pada banyak utas secara bersamaan (namun ini mungkin implementasi yang ditentukan, misalnya: Implementasi Visual Studio adalah utas yang aman )strtok
mengubahstring
itu beroperasi, sehingga tidak dapat digunakan padaconst string
s,const char*
s, atau string literal, untuk tokenize semua ini denganstrtok
atau untuk beroperasi padastring
siapa yang perlu dilestarikan konten,str
harus disalin, maka salinan dapat Dioperasikanc ++ 20 memberi kita
split_view
tokenize string, dengan cara yang tidak merusak: https://topanswers.xyz/cplusplus?q=749#a874Metode sebelumnya tidak dapat menghasilkan tokenized
vector
di tempat, artinya tanpa mengabstraksikannya menjadi fungsi pembantu yang tidak dapat diinisialisasiconst vector<string> tokens
. Fungsionalitas itu dan kemampuan untuk menerima pembatas ruang putih apa pun dapat dimanfaatkan menggunakanistream_iterator
. Misalnya diberikan:const string str{ "The quick \tbrown \nfox" }
kita bisa melakukan ini:Live Example
Diperlukan pembangunan sebuah
istringstream
untuk opsi ini memiliki biaya yang jauh lebih besar daripada 2 opsi sebelumnya, namun biaya ini biasanya tersembunyi dalam biayastring
alokasi.Jika tidak ada opsi di atas yang cukup fleksibel untuk kebutuhan tokenization Anda, opsi yang paling fleksibel adalah dengan menggunakan
regex_token_iterator
tentu saja dengan fleksibilitas ini muncul biaya yang lebih besar, tetapi sekali lagi ini kemungkinan tersembunyi dalamstring
biaya alokasi. Katakan misalnya kita ingin tokenize berdasarkan koma yang tidak diloloskan, juga memakan ruang putih, diberi masukan berikut:const string str{ "The ,qu\\,ick ,\tbrown, fox" }
kita bisa melakukan ini:Live Example
sumber
strtok_s
adalah standar C11, omong-omong.strtok_r
adalah standar POSIX2001. Di antara keduanya, ada versi standarstrtok
untuk sebagian besar platform.#include <cstring>
hanya menyertakan versi c99strtok
. Jadi asumsi saya adalah Anda hanya memberikan komentar ini sebagai bahan pendukung, menunjukkan penerapanstrtok
ekstensi yang spesifik ?strtok_s
disediakan oleh C11 dan sebagai ekstensi mandiri dalam runtime C Microsoft. Ada sedikit sejarah yang aneh di sini di mana_s
fungsi Microsoft menjadi standar C.Saya tahu pertanyaan ini sudah dijawab tetapi saya ingin berkontribusi. Mungkin solusi saya agak sederhana tetapi inilah yang saya buat:
Berikan komentar jika ada pendekatan yang lebih baik untuk sesuatu dalam kode saya atau jika ada sesuatu yang salah.
UPDATE: menambahkan pemisah generik
sumber
Berikut ini pendekatan yang memungkinkan Anda mengontrol apakah token kosong disertakan (seperti strsep) atau dikecualikan (seperti strtok).
sumber
Tampak aneh bagi saya bahwa dengan kita semua kutu buku sadar kecepatan di sini pada SO tidak ada yang menyajikan versi yang menggunakan waktu kompilasi yang dihasilkan mencari tabel untuk pembatas (contoh implementasi lebih jauh ke bawah). Menggunakan tabel pencarian dan iterator harus mengalahkan std :: regex dalam efisiensi, jika Anda tidak perlu mengalahkan regex, cukup gunakan, standarnya seperti pada C ++ 11 dan super fleksibel.
Beberapa sudah menyarankan regex tetapi untuk noobs di sini adalah contoh paket yang harus melakukan persis apa yang diharapkan OP:
Jika kita perlu lebih cepat dan menerima batasan bahwa semua karakter harus 8 bit, kita bisa membuat tabel pencarian pada waktu kompilasi menggunakan metaprogramming:
Dengan itu, membuat
getNextToken
fungsi menjadi mudah:Menggunakannya juga mudah:
Berikut ini adalah contoh langsung: http://ideone.com/GKtkLQ
sumber
Anda dapat memanfaatkan boost :: make_find_iterator. Sesuatu yang mirip dengan ini:
sumber
Inilah Swiss-Army Knife saya yang menggunakan tokenizer string untuk memisahkan string berdasarkan spasi, menghitung string yang dibungkus dengan tanda kutip tunggal dan ganda serta menghilangkan karakter-karakter tersebut dari hasil. Saya menggunakan RegexBuddy 4.x untuk menghasilkan sebagian besar snipet kode, tetapi saya menambahkan penanganan khusus untuk pengupasan kutipan dan beberapa hal lainnya.
sumber
Jika panjang maksimum dari string input yang akan dipatuhi diketahui, seseorang dapat mengeksploitasi ini dan mengimplementasikan versi yang sangat cepat. Saya membuat sketsa ide dasar di bawah ini, yang terinspirasi oleh strtok () dan struktur "suffix array" -data yang dijelaskan Jon Bentley "Programming Perls" edisi ke-2, bab 15. Kelas C ++ dalam hal ini hanya memberikan beberapa organisasi dan kenyamanan penggunaan. Implementasi yang ditunjukkan dapat dengan mudah diperluas untuk menghapus karakter spasi putih terkemuka dan tertinggal di dalam token.
Pada dasarnya seseorang dapat mengganti karakter pemisah dengan karakter penghentian string '\ 0' dan mengatur pointer ke token dengan string yang dimodifikasi. Dalam kasus ekstrim ketika string hanya terdiri dari pemisah, satu mendapat panjang string ditambah 1 yang dihasilkan token kosong. Praktis untuk menduplikasi string yang akan dimodifikasi.
File tajuk:
File implementasi:
Skenario penggunaan adalah:
keluaran:
sumber
boost::tokenizer
adalah teman Anda, tetapi pertimbangkan untuk menjadikan kode Anda portabel dengan mengacu pada masalah internasionalisasi (i18n) dengan menggunakanwstring
/wchar_t
bukan warisanstring
/char
tipe.sumber
wchar_t
merupakan tipe ketergantungan implementasi yang mengerikan yang tidak boleh digunakan siapa pun kecuali benar-benar diperlukan.Kode C ++ sederhana (standar C ++ 98), menerima banyak pembatas (ditentukan dalam std :: string), hanya menggunakan vektor, string, dan iterator.
sumber