Bagaimana cara iterate atas kata-kata dari string?

I'm mencoba untuk iterate atas kata-kata dari sebuah string.

String dapat diasumsikan terdiri dari kata-kata yang dipisahkan oleh spasi.

Perhatikan bahwa I'm tidak tertarik pada C fungsi-fungsi string atau karakter manipulasi/akses. Juga, silakan mendahulukan keanggunan atas efisiensi dalam jawaban anda.

Solusi terbaik yang saya miliki sekarang adalah:

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
    string s = "Somewhere down the road";
    istringstream iss(s);

    do
    {
        string subs;
        iss >> subs;
        cout << "Substring: " << subs << endl;
    } while (iss);
}

Apakah ada cara yang lebih elegan untuk melakukan hal ini?

Mengomentari pertanyaan (9)

Saya menggunakan ini untuk split string dengan delimiter. Pertama menempatkan hasil di pra-dibangun vektor, pengembalian kedua vektor baru.

#include 
#include 
#include 
#include 

template 
void split(const std::string &s, char delim, Out result) {
    std::istringstream iss(s);
    std::string item;
    while (std::getline(iss, item, delim)) {
        *result++ = item;
    }
}

std::vector split(const std::string &s, char delim) {
    std::vector elems;
    split(s, delim, std::back_inserter(elems));
    return elems;
}

Perhatikan bahwa solusi ini tidak melewatkan kosong token, jadi berikut akan menemukan item 4, salah satu yang kosong:

std::vector x = split("one:two::three", ':');
Komentar (26)
Larutan

Untuk apa itu's worth, di sini's cara lain untuk mengekstrak bukti dari sebuah string masukan, hanya mengandalkan standar fasilitas perpustakaan. It's contoh dari kekuatan dan keanggunan di balik desain STL.

#include 
#include 
#include 
#include 
#include 

int main() {
    using namespace std;
    string sentence = "And I feel fine...";
    istringstream iss(sentence);
    copy(istream_iterator(iss),
         istream_iterator(),
         ostream_iterator(cout, "\n"));
}

Alih-alih menyalin diekstrak token ke output stream, yang bisa memasukkan mereka ke dalam sebuah wadah, menggunakan generik yang sama copy algoritma.

vector tokens;
copy(istream_iterator(iss),
     istream_iterator(),
     back_inserter(tokens));

... atau membuat vektor langsung:

vector tokens{istream_iterator{iss},
                      istream_iterator{}};
Komentar (26)

Solusi yang mungkin menggunakan Boost mungkin:

#include 
std::vector strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

Pendekatan ini mungkin bahkan lebih cepat dari stringstream pendekatan. Dan karena ini adalah template generik fungsi ini dapat digunakan untuk membagi jenis lain dari string (wchar, dll. atau UTF-8) menggunakan semua jenis pembatas.

Lihat dokumentasi untuk rincian.

Komentar (20)
#include 
#include 
#include 

int main()
{
    std::string str("Split me by whitespaces");
    std::string buf;                 // Have a buffer string
    std::stringstream ss(str);       // Insert the string into a stream

    std::vector tokens; // Create vector to hold our words

    while (ss >> buf)
        tokens.push_back(buf);

    return 0;
}
Komentar (2)

Untuk orang-orang dengan siapa ia tidak duduk baik untuk mengorbankan semua efisiensi untuk kode ukuran dan lihat "efisien" sebagai jenis keanggunan, berikut ini harus memukul sweet spot (dan saya pikir template wadah kelas awesomely selain elegan.):

template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
              const std::string& delimiters = " ", bool trimEmpty = false)
{
   std::string::size_type pos, lastPos = 0, length = str.length();

   using value_type = typename ContainerT::value_type;
   using size_type  = typename ContainerT::size_type;

   while(lastPos < length + 1)
   {
      pos = str.find_first_of(delimiters, lastPos);
      if(pos == std::string::npos)
      {
         pos = length;
      }

      if(pos != lastPos || !trimEmpty)
         tokens.push_back(value_type(str.data()+lastPos,
               (size_type)pos-lastPos ));

      lastPos = pos + 1;
   }
}

Saya biasanya memilih untuk menggunakan std::vektor<std::string> jenis sebagai parameter kedua (ContainerT)... tapi daftar<> adalah cara yang lebih cepat dari vektor<> ketika akses langsung tidak diperlukan, dan anda bahkan dapat membuat sendiri class string dan menggunakan sesuatu seperti std::daftar<subString> dimana subString tidak melakukan rangkap untuk kecepatan yang luar biasa meningkat.

It's lebih dari dua kali lebih cepat seperti yang tercepat tokenize di halaman ini, dan hampir 5 kali lebih cepat dari beberapa orang lain. Juga dengan pilihan jenis parameter anda dapat menghilangkan semua string dan daftar salinan tambahan untuk meningkatkan kecepatan.

Selain itu tidak melakukan (sangat tidak efisien) kembali dari hasilnya, melainkan melewati token sebagai referensi, sehingga juga memungkinkan anda untuk membangun token menggunakan beberapa panggilan jika anda begitu ingin.

Akhirnya hal ini memungkinkan anda untuk menentukan apakah akan memangkas kosong token dari hasil melalui lalu parameter opsional.

Semua yang dibutuhkan adalah std::string... sisanya adalah opsional. Tidak menggunakan sungai atau dorongan perpustakaan, tetapi cukup fleksibel untuk dapat menerima beberapa asing jenis secara alami.

Komentar (10)

Berikut ini's solusi lain. It's kompak dan cukup efisien:

std::vector split(const std::string &text, char sep) {
  std::vector tokens;
  std::size_t start = 0, end = 0;
  while ((end = text.find(sep, start)) != std::string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = end + 1;
  }
  tokens.push_back(text.substr(start));
  return tokens;
}

Hal ini dapat dengan mudah menjadi templatised untuk menangani string pemisah, lebar string, dll.

Perhatikan bahwa membelah "," hasil dalam satu string kosong dan membelah "," (ie. sep) hasil di dua string kosong.

Hal ini juga dapat dengan mudah diperluas untuk melewatkan kosong token:

std::vector split(const std::string &text, char sep) {
    std::vector tokens;
    std::size_t start = 0, end = 0;
    while ((end = text.find(sep, start)) != std::string::npos) {
        if (end != start) {
          tokens.push_back(text.substr(start, end - start));
        }
        start = end + 1;
    }
    if (end != start) {
       tokens.push_back(text.substr(start));
    }
    return tokens;
}

Jika membelah string di beberapa pembatas sambil melompat-lompat kosong token yang diinginkan, versi ini dapat digunakan:

std::vector split(const std::string& text, const std::string& delims)
{
    std::vector tokens;
    std::size_t start = text.find_first_not_of(delims), end = 0;

    while((end = text.find_first_of(delims, start)) != std::string::npos)
    {
        tokens.push_back(text.substr(start, end - start));
        start = text.find_first_not_of(delims, end);
    }
    if(start != std::string::npos)
        tokens.push_back(text.substr(start));

    return tokens;
}
Komentar (14)

Ini adalah cara favorit saya untuk iterate melalui sebuah string. Anda dapat melakukan apapun yang anda inginkan per kata.

string line = "a line of text to iterate through";
string word;

istringstream iss(line, istringstream::in);

while( iss >> word )     
{
    // Do something on `word` here...
}
Komentar (3)

Hal ini serupa dengan Stack Overflow pertanyaan [Bagaimana saya tokenize string di C++?][1].


#include 
#include 
#include 

using namespace std;
using namespace boost;

int main(int argc, char** argv)
{
    string text = "token  test\tstring";

    char_separator sep(" \t");
    tokenizer tokens(text, sep);
    for (const string& t : tokens)
    {
        cout 
Komentar (1)

Aku seperti berikut karena menempatkan hasil dalam vektor, mendukung string sebagai pembatas dan memberikan kontrol lebih menjaga nilai-nilai kosong. Tapi, itu doesn't terlihat baik kemudian.

#include 
#include 
#include 
#include 
#include 
using namespace std;

vector split(const string& s, const string& delim, const bool keep_empty = true) {
    vector result;
    if (delim.empty()) {
        result.push_back(s);
        return result;
    }
    string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = search(substart, s.end(), delim.begin(), delim.end());
        string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.push_back(temp);
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

int main() {
    const vector words = split("So close no matter how far", " ");
    copy(words.begin(), words.end(), ostream_iterator(cout, "\n"));
}

Tentu saja, Meningkatkan memiliki split() yang bekerja sebagian seperti itu. Dan, jika dengan 'white-space', anda benar-benar tidak berarti semua jenis white-space, menggunakan Boost's split dengan is_any_of() karya besar.

Komentar (1)

STL tidak memiliki metode tersebut sudah tersedia.

Namun, anda dapat menggunakan C's strtok() fungsi dengan menggunakan std::string::c_str() anggota, atau anda dapat menulis anda sendiri. Berikut ini adalah contoh kode yang saya temukan setelah pencarian Google cepat ("STL string split"):

void Tokenize(const string& str,
              vector& tokens,
              const string& delimiters = " ")
{
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

Diambil dari: http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

Jika anda memiliki pertanyaan tentang kode sampel, meninggalkan komentar dan saya akan menjelaskan.

Dan hanya karena tidak menerapkan typedef disebut iterator atau membebani << operator tidak berarti itu adalah kode yang buruk. Saya menggunakan fungsi C cukup sering. Misalnya, printf dan scanf kedua lebih cepat dari std::cin dan std::cout (signifikan), yang fopen syntax ini jauh lebih ramah untuk biner jenis, dan mereka juga cenderung menghasilkan lebih kecil Ongkos.

Don't mendapatkan yang dijual ini "Keanggunan atas kinerja" kesepakatan.

Komentar (10)

Berikut ini adalah membagi fungsi yang:

  • generic
  • menggunakan C++ standar (tidak ada dorongan)
  • menerima beberapa pembatas
  • mengabaikan kosong token (dapat dengan mudah diubah)

template vektor split(const T & str, const T & pembatas) { vektor v; typename T::size_type mulai = 0; auto pos = str.find_first_of(pembatas, mulai); sementara(pos != T::npo) { jika(pos != start) // abaikan kosong token v. emplace_back(str, mulai, pos - start); start = pos + 1; pos = str.find_first_of(pembatas, mulai); } jika(start < str.length()) // abaikan tertinggal pembatas v. emplace_back(str, mulai, str.length() - start); // menambahkan apa yang's kiri string kembali v; }

Contoh penggunaan:

    vector v = split("Hello, there; World", ";,");
    vector v = split(L"Hello, there; World", L";,");
Komentar (7)

Saya memiliki 2 jalur solusi untuk masalah ini:


char sep = ' ';
std::string s="1 This is an example";

for(size_t p=0, q=0; p!=s.npos; p=q)
  std::cout 
Komentar (0)

Namun lain fleksibel dan cepat cara

template
void tokenize(Operator& op, const char* input, const char* delimiters) {
  const char* s = input;
  const char* e = s;
  while (*e != 0) {
    e = s;
    while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
    if (e - s > 0) {
      op(s, e - s);
    }
    s = e + 1;
  }
}

Untuk menggunakannya dengan vektor string (Edit: Karena seseorang menunjukkan tidak mewarisi kelas STL... hrmf ;) ) :

template
class Appender {
public:
  Appender(ContainerType& container) : container_(container) {;}
  void operator() (const char* s, unsigned length) { 
    container_.push_back(std::string(s,length));
  }
private:
  ContainerType& container_;
};

std::vector strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");

Yang's itu! Dan yang's hanya salah satu cara untuk menggunakan tokenizer, seperti bagaimana hanya menghitung kata-kata:

class WordCounter {
public:
  WordCounter() : noOfWords(0) {}
  void operator() (const char*, unsigned) {
    ++noOfWords;
  }
  unsigned noOfWords;
};

WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t"); 
ASSERT( wc.noOfWords == 7 );

Dibatasi oleh imajinasi ;)

Komentar (1)

Berikut ini's solusi sederhana yang hanya menggunakan standar regex perpustakaan

#include 
#include 
#include 

std::vector Tokenize( const string str, const std::regex regex )
{
    using namespace std;

    std::vector result;

    sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
    sregex_token_iterator reg_end;

    for ( ; it != reg_end; ++it ) {
        if ( !it->str().empty() ) //token could be empty:check
            result.emplace_back( it->str() );
    }

    return result;
}

Regex argumen memungkinkan memeriksa beberapa argumen (spasi, koma, dll.)

Saya biasanya hanya memeriksa untuk split pada ruang dan koma, jadi saya juga memiliki fungsi default:

std::vector TokenizeDefault( const string str )
{
    using namespace std;

    regex re( "[\\s,]+" );

    return Tokenize( str, re );
}

The " [\\\\\s,]+" cek untuk spasi (\\\\\s) dan koma (,).

Catatan, jika anda ingin membagi wstring bukan string,

  • mengubah semua std::regex untuk std::wregex
  • mengubah semua sregex_token_iterator untuk wsregex_token_iterator

Catatan, anda mungkin juga ingin mengambil string argumen dengan referensi, tergantung pada compiler.

Komentar (3)

Menggunakan std::stringstream seperti yang anda telah bekerja baik-baik saja, dan lakukan apa yang anda ingin. Jika anda're hanya mencari cara yang berbeda dalam melakukan sesuatu meskipun, anda dapat menggunakan [std::cari()][1]/[std::find_first_of()][2] dan [std::string::substr()][3].

Berikut ini's contoh:


#include 
#include 

int main()
{
    std::string s("Somewhere down the road");
    std::string::size_type prev_pos = 0, pos = 0;

    while( (pos = s.find(' ', pos)) != std::string::npos )
    {
        std::string substring( s.substr(prev_pos, pos-prev_pos) );

        std::cout 
Komentar (1)

Jika anda ingin menggunakan boost, tapi ingin menggunakan seluruh string sebagai pembatas (bukan karakter tunggal seperti di sebagian besar dari sebelumnya mengusulkan solusi), anda dapat menggunakan boost_split_iterator.

Contoh kode termasuk perangkat template:

#include 
#include 
#include 

template
inline void split(
    const std::string& str, 
    const std::string& delim, 
    _OutputIterator result)
{
    using namespace boost::algorithm;
    typedef split_iterator It;

    for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
            iter!=It();
            ++iter)
    {
        *(result++) = boost::copy_range(*iter);
    }
}

int main(int argc, char* argv[])
{
    using namespace std;

    vector splitted;
    split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));

    // or directly to console, for example
    split("HelloFOOworldFOO!", "FOO", ostream_iterator(cout, "\n"));
    return 0;
}
Komentar (0)

Ada sebuah fungsi bernama strtok.

#include
using namespace std;

vector split(char* str,const char* delim)
{
    char* saveptr;
    char* token = strtok_r(str,delim,&saveptr);

    vector result;

    while(token != NULL)
    {
        result.push_back(token);
        token = strtok_r(NULL,delim,&saveptr);
    }
    return result;
}
Komentar (7)

Heres regex solusi yang hanya menggunakan standar regex perpustakaan. (Saya'm sedikit berkarat, jadi mungkin ada beberapa kesalahan sintaks, tapi ini setidaknya gambaran umum)

#include 
#include 
#include 

using namespace std;

vector split(string s){
    regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
    regex_iterator rit ( s.begin(), s.end(), r );
    regex_iterator rend; //iterators to iterate thru words
    vector result(rit, rend);
    return result;  //iterates through the matches to fill the vector
}
Komentar (1)

The stringstream dapat nyaman jika anda perlu untuk mengurai string dengan non-ruang simbol-simbol:

string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;

istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')
Komentar (0)

Sejauh ini saya menggunakan salah satu di [Meningkatkan][1], tapi aku butuh sesuatu yang doesn't tergantung pada hal itu, jadi aku datang untuk ini:


static void Split(std::vector& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
    std::ostringstream word;
    for (size_t n = 0; n < input.size(); ++n)
    {
        if (std::string::npos == separators.find(input[n]))
            word 
Komentar (0)