Bagaimana cara memberi tokenize pada C ++?

414

Java memiliki metode split yang mudah:

String str = "The quick brown fox";
String[] results = str.split(" ");

Apakah ada cara mudah untuk melakukan ini di C ++?

Bill the Lizard
sumber
172
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 ..
Paschalis
1
Kemungkinan duplikat Memecah string dalam C ++?
KOB
2
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:

auto const str = "The quick brown fox"s;
auto const re = std::regex{R"(\s+)"};
auto const vec = std::vector<std::string>(
    std::sregex_token_iterator{begin(str), end(str), re, -1},
    std::sregex_token_iterator{}
);
Konrad Rudolph
sumber
53
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>

using namespace std;
using namespace 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>

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

    char_separator<char> sep(", ");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const auto& t : tokens) {
        cout << t << "." << endl;
    }
}
Ferruccio
sumber
1
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>
using namespace std;

vector<string> split(const char *str, char c = ' ')
{
    vector<string> result;

    do
    {
        const char *begin = str;

        while(*str != c && *str)
            str++;

        result.push_back(string(begin, str));
    } while (0 != *str++);

    return result;
}
Adam Pierce
sumber
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 :-)

Menandai
sumber
42
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:

stringstream ss("bla bla");
string s;

while (getline(ss, s, ' ')) {
 cout << s << endl;
}

Jika mau, Anda dapat membuat split()metode sederhana untuk mengembalikan vector<string>, yang sangat berguna.

pengguna35978
sumber
2
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);
}
KeithB
sumber
17
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) << "\"" << endl

int
main()
{
    vector<string> v;

    split( v, "A:PEP:909:Inventory Item", ":" );

    for (unsigned int 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!)

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.)

Mr.Ree
sumber
12
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>

using namespace 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;
    }
}
wb
sumber
5
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.
Marek R
35

Boost memiliki fungsi pemisahan yang kuat: boost :: algoritme :: split .

Program sampel:

#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 (const auto& field : fields)
        std::cout << "\"" << field << "\"\n";
    return 0;
}

Keluaran:

"a"
"b"
" c "
""
"e"
"f"
""
Raz
sumber
26

Saya tahu Anda meminta solusi C ++, tetapi Anda mungkin menganggap ini berguna:

Qt

#include <QString>

...

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 

Keuntungan dari Boost dalam contoh ini adalah pemetaan langsung ke kode pos Anda.

Lihat lebih lanjut di dokumentasi Qt

sivabudh
sumber
22

Berikut adalah contoh tokenizer kelas yang mungkin melakukan apa yang Anda inginkan

//Header file
class Tokenizer 
{
    public:
        static const std::string DELIMITERS;
        Tokenizer(const std::string& str);
        Tokenizer(const std::string& str, const std::string& delimiters);
        bool NextToken();
        bool NextToken(const std::string& delimiters);
        const std::string GetToken() const;
        void Reset();
    protected:
        size_t m_offset;
        const std::string m_string;
        std::string m_token;
        std::string m_delimiters;
};

//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");

Tokenizer::Tokenizer(const std::string& s) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(DELIMITERS) {}

Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(delimiters) {}

bool Tokenizer::NextToken() 
{
    return NextToken(m_delimiters);
}

bool Tokenizer::NextToken(const std::string& delimiters) 
{
    size_t i = m_string.find_first_not_of(delimiters, m_offset);
    if (std::string::npos == i) 
    {
        m_offset = m_string.length();
        return false;
    }

    size_t j = m_string.find_first_of(delimiters, i);
    if (std::string::npos == j) 
    {
        m_token = m_string.substr(i);
        m_offset = m_string.length();
        return true;
    }

    m_token = m_string.substr(i, j - i);
    m_offset = j;
    return true;
}

Contoh:

std::vector <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
    v.push_back(s.GetToken());
}
vzczc
sumber
19

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);
    }
}

Cobalah langsung !

Parham
sumber
3
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, "-");
dbr
sumber
3
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>

const char *whitespace  = " \t\r\n\f";
const char *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;
       }
    }

    return 0;
}
DannyK
sumber
8

Lihat contoh ini. Mungkin membantu Anda ..

#include <iostream>
#include <sstream>

using namespace std;

int main ()
{
    string tmps;
    istringstream is ("the dellimiter is the space");
    while (is.good ()) {
        is >> tmps;
        cout << tmps << "\n";
    }
    return 0;
}
sohesado
sumber
1
Saya akan melakukanwhile ( is >> tmps ) { std::cout << tmps << "\n"; }
jordix
6

MFC / ATL memiliki tokenizer yang sangat bagus. Dari MSDN:

CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;

resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
   printf("Resulting token: %s\n", resToken);
   resToken= str.Tokenize("% #",curPos);
};

Output

Resulting Token: First
Resulting Token: Second
Resulting Token: Third
Jim In Texas
sumber
1
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.

Di Freund
sumber
3
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:

unsigned TokenizeString(const std::string& i_source,
                        const std::string& i_seperators,
                        bool i_discard_empty_tokens,
                        std::vector<std::string>& o_tokens)
{
    unsigned prev_pos = 0;
    unsigned pos = 0;
    unsigned number_of_tokens = 0;
    o_tokens.clear();
    pos = i_source.find_first_of(i_seperators, pos);
    while (pos != std::string::npos)
    {
        std::string token = i_source.substr(prev_pos, pos - prev_pos);
        if (!i_discard_empty_tokens || token != "")
        {
            o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
            number_of_tokens++;
        }

        pos++;
        prev_pos = pos;
        pos = i_source.find_first_of(i_seperators, pos);
    }

    if (prev_pos < i_source.length())
    {
        o_tokens.push_back(i_source.substr(prev_pos));
        number_of_tokens++;
    }

    return number_of_tokens;
}

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.

jilles de wit
sumber
4

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).

Fawix
sumber
+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:

using namespace std;

string someText = ...

string::size_type tokenOff = 0, sepOff = tokenOff;
while (sepOff != string::npos)
{
    sepOff = someText.find(' ', sepOff);
    string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff;
    string token = someText.substr(tokenOff, tokenLen);
    if (!token.empty())
        /* do something with token */;
    tokenOff = sepOff;
}
David919
sumber
4

Saya pikir itulah gunanya >>operator pada string stream:

string word; sin >> word;
Daren Thomas
sumber
1
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:

auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };

while (start != cend(str)) {
    const auto finish = find(++start, cend(str), ' ');

    tokens.push_back(string(start, finish));
    start = finish;
}

Live Example


Jika Anda mencari kompleksitas abstrak dengan menggunakan fungsionalitas standar, seperti yang disarankan On Freund strtok 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:

  1. 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 )
  2. 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 )
  3. 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

memberi kita split_view tokenize string, dengan cara yang tidak merusak: https://topanswers.xyz/cplusplus?q=749#a874


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:

istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };

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 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:

const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };

Live Example

Jonathan Mee
sumber
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:

vector<string> get_words(string const& text, string const& separator)
{
    vector<string> result;
    string tmp = text;

    size_t first_pos = 0;
    size_t second_pos = tmp.find(separator);

    while (second_pos != string::npos)
    {
        if (first_pos != second_pos)
        {
            string word = tmp.substr(first_pos, second_pos - first_pos);
            result.push_back(word);
        }
        tmp = tmp.substr(second_pos + separator.length());
        second_pos = tmp.find(separator);
    }

    result.push_back(tmp);

    return result;
}

Berikan komentar jika ada pendekatan yang lebih baik untuk sesuatu dalam kode saya atau jika ada sesuatu yang salah.

UPDATE: menambahkan pemisah generik

Alat pemecah buah keras
sumber
Menggunakan solusi Anda dari kerumunan :) Dapatkah saya mengubah kode Anda untuk menambahkan pemisah apa pun?
Zac
1
@Zac senang Anda menyukainya dan ofc Anda dapat memodifikasinya ... tambahkan saja bagian pembaruan yang berani ke jawaban saya ...
NutCracker
2

Berikut ini pendekatan yang memungkinkan Anda mengontrol apakah token kosong disertakan (seperti strsep) atau dikecualikan (seperti strtok).

#include <string.h> // for strchr and strlen

/*
 * want_empty_tokens==true  : include empty tokens, like strsep()
 * want_empty_tokens==false : exclude empty tokens, like strtok()
 */
std::vector<std::string> tokenize(const char* src,
                                  char delim,
                                  bool want_empty_tokens)
{
  std::vector<std::string> tokens;

  if (src and *src != '\0') // defensive
    while( true )  {
      const char* d = strchr(src, delim);
      size_t len = (d)? d-src : strlen(src);

      if (len or want_empty_tokens)
        tokens.push_back( std::string(src, len) ); // capture token

      if (d) src += len+1; else break;
    }

  return tokens;
}
Darren Smith
sumber
2

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 version
    return 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(const auto&s:v){
        std::cout << s << std::endl;
    }
    std::cout << "crazy version:" << std::endl;
    v = split(str, std::regex{"[^e]+"});  //using e as delim shows flexibility
    for(const auto&s:v){
        std::cout << s << std::endl;
    }
    return 0;
}

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...> struct BoolSequence{};        //just here to hold bools
template<char...> struct CharSequence{};        //just here to hold chars
template<typename T, char C> struct Contains;   //generic
template<char First, char... Cs, char Match>    //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
    Contains<CharSequence<Cs...>, Match>{};     //strip first and increase index
template<char First, char... Cs>                //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {}; 
template<char Match>                            //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};

template<int I, typename T, typename U> 
struct MakeSequence;                            //generic
template<int I, bool... Bs, typename U> 
struct MakeSequence<I,BoolSequence<Bs...>, U>:  //not last
    MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U> 
struct MakeSequence<0,BoolSequence<Bs...>,U>{   //last  
    using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
    /* could be made constexpr but not yet supported by MSVC */
    static bool isDelim(const char c){
        static const bool table[256] = {Bs...};
        return table[static_cast<int>(c)];
    }   
};
using Delims = CharSequence<'.',',',' ',':','\n'>;  //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<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 end
    auto second = std::find_if(begin,end,Table{});      //find first delim or end
    return 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;
    }
    return 0;
}

Berikut ini adalah contoh langsung: http://ideone.com/GKtkLQ

odinthenerd
sumber
1
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>
inline vector< basic_string<CH> > tokenize(
    const basic_string<CH> &Input,
    const basic_string<CH> &Delimiter,
    bool remove_empty_token
    ) {

    typedef typename basic_string<CH>::const_iterator string_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()));
        }
        else
            Result.push_back(basic_string<CH>(it,i->begin()));
        it = i->end();
    }
    if(it != it_end)
        Result.push_back(basic_string<CH>(it,it_end));

    return Result;
}
Arash
sumber
1

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.

#include <string>
#include <locale>
#include <regex>

std::vector<std::wstring> tokenize_string(std::wstring string_to_tokenize) {
    std::vector<std::wstring> tokens;

    std::wregex re(LR"(("[^"]*"|'[^']*'|[^"' ]+))", std::regex_constants::collate);

    std::wsregex_iterator next( string_to_tokenize.begin(),
                                string_to_tokenize.end(),
                                re,
                                std::regex_constants::match_not_null );

    std::wsregex_iterator end;
    const wchar_t single_quote = L'\'';
    const wchar_t double_quote = L'\"';
    while ( next != end ) {
        std::wsmatch match = *next;
        const std::wstring token = match.str( 0 );
        next++;

        if (token.length() > 2 && (token.front() == double_quote || token.front() == single_quote))
            tokens.emplace_back( std::wstring(token.begin()+1, token.begin()+token.length()-1) );
        else
            tokens.emplace_back(token);
    }
    return tokens;
}
kayleeFrye_onDeck
sumber
1
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:

class TextLineSplitter
{
public:

    TextLineSplitter( const size_t max_line_len );

    ~TextLineSplitter();

    void            SplitLine( const char *line,
                               const char sep_char = ',',
                             );

    inline size_t   NumTokens( void ) const
    {
        return mNumTokens;
    }

    const char *    GetToken( const size_t token_idx ) const
    {
        assert( token_idx < mNumTokens );
        return mTokens[ token_idx ];
    }

private:
    const size_t    mStorageSize;

    char           *mBuff;
    char          **mTokens;
    size_t          mNumTokens;

    inline void     ResetContent( void )
    {
        memset( mBuff, 0, mStorageSize );
        // mark all items as empty:
        memset( mTokens, 0, mStorageSize * sizeof( char* ) );
        // reset counter for found items:
        mNumTokens = 0L;
    }
};

File implementasi:

TextLineSplitter::TextLineSplitter( const size_t max_line_len ):
    mStorageSize ( max_line_len + 1L )
{
    // allocate memory
    mBuff   = new char  [ mStorageSize ];
    mTokens = new char* [ mStorageSize ];

    ResetContent();
}

TextLineSplitter::~TextLineSplitter()
{
    delete [] mBuff;
    delete [] mTokens;
}


void TextLineSplitter::SplitLine( const char *line,
                                  const char sep_char   /* = ',' */,
                                )
{
    assert( sep_char != '\0' );

    ResetContent();
    strncpy( mBuff, line, mMaxLineLen );

    size_t idx       = 0L; // running index for characters

    do
    {
        assert( idx < mStorageSize );

        const char chr = line[ idx ]; // retrieve current character

        if( mTokens[ mNumTokens ] == NULL )
        {
            mTokens[ mNumTokens ] = &mBuff[ idx ];
        } // if

        if( chr == sep_char || chr == '\0' )
        { // item or line finished
            // overwrite separator with a 0-terminating character:
            mBuff[ idx ] = '\0';
            // count-up items:
            mNumTokens ++;
        } // if

    } while( line[ idx++ ] );
}

Skenario penggunaan adalah:

// 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 ) );
}

keluaran:

Item1

Item2
Item3
Malaikat Sinigersky
sumber
0

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>

using namespace std;
using namespace 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 characters
    Tok 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";
  }
  return 0;
}
jochenleidner
sumber
"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 token
        if (!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;
}
vsoftco
sumber