Pertanyaan Cara paling elegan untuk mengulang kata-kata string [tertutup]


Apa cara paling elegan untuk mengulang kata-kata string? String dapat diasumsikan terdiri dari kata-kata yang dipisahkan oleh spasi.

Perhatikan bahwa saya tidak tertarik dengan fungsi string C atau jenis manipulasi karakter / akses. Juga, mohon diutamakan keanggunan atas efisiensi dalam jawaban Anda.

Solusi terbaik yang saya miliki saat ini 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);
}

2638


asal


Jawaban:


Untuk apa nilainya, inilah cara lain untuk mengekstrak token dari string input, hanya mengandalkan fasilitas pustaka standar. Ini adalah contoh kekuatan dan keanggunan di balik desain STL.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

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

Alih-alih menyalin token yang diekstrak ke aliran keluaran, seseorang dapat memasukkannya ke dalam wadah, menggunakan generik yang sama copy algoritma.

vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));

... atau buat vector langsung:

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};

1188



Saya menggunakan ini untuk memisahkan string dengan pemisah. Yang pertama menempatkan hasil dalam vektor pra-konstruksi, yang kedua mengembalikan vektor baru.

#include <string>
#include <sstream>
#include <vector>
#include <iterator>

template<typename Out>
void split(const std::string &s, char delim, Out result) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        *(result++) = item;
    }
}

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

Perhatikan bahwa solusi ini tidak melewati token kosong, sehingga yang berikut ini akan menemukan 4 item, salah satunya kosong:

std::vector<std::string> x = split("one:two::three", ':');

2307



Solusi yang mungkin menggunakan Boost mungkin:

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

Pendekatan ini mungkin lebih cepat daripada stringstream pendekatan. Dan karena ini adalah fungsi template generik, ia dapat digunakan untuk memisahkan jenis string lain (wchar, dll. Atau UTF-8) menggunakan semua jenis pembatas.

Lihat dokumentasi untuk detailnya.


794



#include <vector>
#include <string>
#include <sstream>

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<std::string> tokens; // Create vector to hold our words

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

    return 0;
}

321



Bagi mereka yang tidak duduk dengan baik untuk mengorbankan semua efisiensi untuk ukuran kode dan melihat "efisien" sebagai jenis keanggunan, hal berikut ini harus mencapai titik yang manis (dan saya pikir kelas kontainer template adalah tambahan yang sangat 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::vector<std::string> jenis sebagai parameter kedua saya (ContainerT) ... tapi list<> jauh lebih cepat daripada vector<> karena ketika akses langsung tidak diperlukan, dan Anda bahkan dapat membuat kelas string Anda sendiri dan menggunakan sesuatu seperti std::list<subString> dimana subString tidak melakukan salinan untuk peningkatan kecepatan yang luar biasa.

Ini lebih dari dua kali lipat secepat yang tercepat di halaman ini dan hampir 5 kali lebih cepat daripada yang lain. Juga dengan tipe parameter yang sempurna Anda dapat menghilangkan semua salinan string dan daftar untuk peningkatan kecepatan tambahan.

Selain itu, ia tidak melakukan pengembalian hasil yang sangat tidak efisien, tetapi ia melewatkan token sebagai referensi, sehingga juga memungkinkan Anda untuk membangun token menggunakan beberapa panggilan jika Anda menginginkannya.

Terakhir, Anda dapat menentukan apakah akan memangkas token kosong dari hasil melalui parameter opsional terakhir.

Yang dibutuhkan hanyalah std::string... sisanya opsional. Ia tidak menggunakan aliran atau pustaka dorongan, tetapi cukup fleksibel untuk dapat menerima beberapa jenis asing ini secara alami.


168



Inilah solusi lain. Ringkas dan cukup efisien:

std::vector<std::string> split(const std::string &text, char sep) {
  std::vector<std::string> 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;
}

Ini dapat dengan mudah templatised untuk menangani pemisah string, string lebar, dll.

Perhatikan bahwa membelah "" menghasilkan string kosong tunggal dan membelah "," (mis. sep) menghasilkan dua string kosong.

Ini juga dapat dengan mudah diperluas untuk melewati token kosong:

std::vector<std::string> split(const std::string &text, char sep) {
    std::vector<std::string> 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 memisahkan string di beberapa pembatas saat melewatkan token kosong yang diinginkan, versi ini dapat digunakan:

std::vector<std::string> split(const std::string& text, const std::string& delims)
{
    std::vector<std::string> 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;
}

150



Ini adalah cara favorit saya untuk mengulang melalui string. Anda dapat melakukan apa pun 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...
}

106



Ini mirip dengan pertanyaan Stack Overflow Bagaimana saya mengesahkan string dalam C ++?.

#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

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

    char_separator<char> sep(" \t");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const string& t : tokens)
    {
        cout << t << "." << endl;
    }
}

76



Saya suka yang berikut karena menempatkan hasil ke dalam vektor, mendukung string sebagai delim dan memberikan kontrol atas menjaga nilai kosong. Tapi, itu tidak terlihat bagus.

#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
    vector<string> 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<string> words = split("So close no matter how far", " ");
    copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}

Tentu saja, Boost memiliki split() yang bekerja sebagian seperti itu. Dan, jika dengan 'ruang putih', Anda benar-benar berarti semua jenis ruang-putih, menggunakan perpecahan Boost dengan is_any_of() bekerja dengan baik.


65