Cum pot repeta peste cuvintele dintr-un șir?

Am'm încercarea de a repeta peste cuvintele dintr-un șir.

Șirul poate fi presupus a fi compus din cuvinte separate prin spatii.

Rețineți că m-am'm nu este interesat în șir C funcții sau ca un fel de personaj de manipulare/de acces. De asemenea, vă rugăm să dați prioritate eleganta peste eficiență în răspunsul dumneavoastră.

Cea mai bună soluție pe care o am acum este:

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

Există un mod mai elegant de a face acest lucru?

Comentarii la întrebare (9)

Eu folosesc acest lucru pentru a împărți șirul de un delimitator. Prima plasează rezultatele într-un pre-construite vector, cea de-a doua returnează un vector nou.

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

Rețineți că această soluție nu sări gol jetoane, astfel încât următoarele 4 elemente, dintre care unul este gol:

std::vector x = split("one:two::three", ':');
Comentarii (26)
Soluția

Pentru ce-l's valoare, aici's un alt mod de a extrage jetoane dintr-un șir de intrare, bazându-se doar pe standard, facilități de bibliotecă. L's un exemplu de putere si eleganta spatele design-ul din 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"));
}

În loc de copierea extras jetoane la un flux de ieșire, s-ar putea introduceți-le într-un recipient, folosind același generic ["copie"] (https://en.cppreference.com/w/cpp/algorithm/copy) algoritm.

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

... sau de a crea "vector" direct:

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

O posibilă soluție folosind Impuls ar putea fi:

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

Această abordare ar putea fi chiar mai repede decât stringstream abordare. Și din moment ce acesta este un șablon generic funcția poate fi folosit pentru a împărți alte tipuri de siruri de caractere (wchar, etc. sau UTF-8), folosind toate tipurile de delimitatori.

Vezi documentația pentru detalii.

Comentarii (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;
}
Comentarii (2)

Pentru cei cu care nu sta bine să-și sacrifice toate eficiență pentru codul de dimensiunea și de a vedea "eficient" ca un tip de eleganta, următoarele ar trebui să atingă un loc dulce (și cred că modelul clasa container este o awesomely plus de eleganta.):

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

Eu de obicei aleg să folosească std::vector<std::string> tipuri de ca al doilea parametru (ContainerT)... dar lista<> este mult mai rapid decât vector<> pentru accesul direct nu este necesar, și puteți crea chiar propriile clasa string și de a folosi ceva de genulstd::list<subșir>"unde" subșir` nu face nici copii pentru o viteză incredibilă crește.

L's mai mult decat dublu la fel de repede ca cel mai rapid tokenize pe această pagină și de aproape 5 ori mai repede decât altele. De asemenea, perfect cu tipul parametrilor puteți elimina toate șir și lista de exemplare suplimentare pentru a crește viteza.

În plus, acesta nu face (extrem de ineficient) întoarcere de rezultat, ci mai degrabă se trece la jetoane ca o referință, astfel, de asemenea, permițându-vă pentru a construi jetoane folosind apelurile multiple dacă ați dorit.

În cele din urmă vă permite să specificați dacă doriți să tăiați gol token-uri de rezultate, prin intermediul unei ultimul parametru opțional.

Tot ce trebuie e std::string... restul sunt opționale. Nu se folosesc fluxuri sau impuls de bibliotecă, dar este suficient de flexibil pentru a fi în măsură să accepte unele dintre aceste tipuri de externe în mod natural.

Comentarii (10)

Aici's o altă soluție. L's compact și suficient de eficiente:

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

Acesta poate fi ușor templatised să se ocupe șir separatoare, larg siruri de caractere, etc.

Rețineți că divizarea "" rezultatele într-un singur șir gol și divizarea","` (ie. sep) rezultatele în două șiruri goale.

De asemenea, acesta poate fi extins cu ușurință pentru a sări gol jetoane:

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

Dacă divizarea unui șir la mai multe delimitatori în timp ce sărind peste gol jetoane este de dorit, această versiune poate fi folosit:

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;
}
Comentarii (14)

Acesta este modul meu preferat de a repeta printr-un șir. Poți să faci ce vrei pe cuvânt.

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

istringstream iss(line, istringstream::in);

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

Acest lucru este similar cu Stiva Preaplin întrebare [Cum am tokenize un șir de caractere în 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 
Comentarii (1)

Îmi place următorul deoarece plasează rezultatele într-un vector, susține un șir ca un delim și oferă control asupra păstrând valorile goale. Dar, nu't arata la fel de bine atunci.

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

Desigur, pentru a Stimula are o split(), care funcționează parțial ca asta. Și, dacă prin 'white-space', tu chiar nu însemna orice tip de white-space, folosind Impuls's split cu is_any_of()` mare de lucrări.

Comentarii (1)

STL nu au o astfel de metodă disponibile deja.

Cu toate acestea, puteți utiliza fie C's strtok() funcție de utilizarea std::string::c_str() membru, sau puteți scrie propriul. Aici este un exemplu de cod am găsit după o căutare rapidă pe Google ("STL șir 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);
    }
}

Preluat de pe: <http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++de Programare-HOWTO-7.html>

Dacă aveți întrebări cu privire la codul de probă, lăsați un comentariu și voi explica.

Și doar pentru că nu pune în aplicare o typedef numit iterator sau suprasarcină << operatorul nu înseamnă că este rău cod. Eu folosesc C funcții destul de frecvent. De exemplu, printf și scanf atât sunt mai rapide decât std::cin și std::cout (în mod semnificativ), de fopen sintaxa este mult mai prietenos pentru binar tipuri, și ele, de asemenea, tind să producă mai mici Fostele.

Don't obține vândute pe acest "Eleganta peste performanta" afacere.

Comentarii (10)

Aici este o funcție split că:

  • este generic
  • foloseste standardul C++ (fara boost)
  • acceptă mai multe delimitatori
  • ignoră gol jetoane (poate fi ușor schimbat)

șablon vector split(const T & str, const T & delimitatori) { vector v; typename T::size_type start = 0; auto pos = str.find_first_of(delimitatori, începe); în timp ce(pos != T::onp) { dacă(pos != începe) // ignora gol jetoane v. emplace_back(str, start, pos - start); start = poz + 1; pos = str.find_first_of(delimitatori, începe); } dacă(începe < str.lungime()) // ignora delimitator de sfârșit v. emplace_back(str, start, str.lungime() - start); // adauga ceea ce's de stânga a șirului return v; }

Exemplu de utilizare:

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

Am 2 linii soluție la această problemă:


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

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

Încă un alt flexibil și rapid

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

Să-l folosească cu un vector de siruri de caractere (Edit: Deoarece cineva mi-a arătat să nu moștenească STL clase... 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");

Ca's a! Și că's doar o modalitate de a folosi tokenizer, cum să contele de cuvinte:

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

Limitată de imaginația ;)

Comentarii (1)

Aici's o soluție simplă care folosește numai standard regex bibliotecă

#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 argument permite verificarea pentru mai multe argumente (spații, virgule, etc.)

Eu, de obicei, doar verifica pentru a împărți pe spații și virgule, așa că am, de asemenea, această funcție implicită:

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

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

    return Tokenize( str, re );
}

La "[\\s]+" verificări pentru spații (\s) si virgula (,`).

Notă, dacă doriți să împartă wstring în loc de string,

  • schimba toate std::regex " la " std::wregex
  • schimba toate sregex_token_iterator " la " wsregex_token_iterator

Rețineți, de asemenea, poate doriți să ia șir argumentul de referință, în funcție de compilator.

Comentarii (3)

Folosind std::stringstream ca trebuie funcționează foarte bine, și de a face exact ceea ce ai vrut. Daca're în căutarea doar pentru mod diferit de a face lucrurile, deși, puteți utiliza [std::găsi()][1]/[std::find_first_of()][2] și [std::string::substr()][3].

Aici's un exemplu:


#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 
Comentarii (1)

Dacă doriți să utilizați impuls, dar doriți să utilizați un șir întreg ca delimitator (în loc de personaje unice la fel ca în cele mai multe dintre cele propuse anterior soluții), puteți utiliza boost_split_iterator.

Exemplu de cod, inclusiv convenabil șablon:

#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;
}
Comentarii (0)

Există o funcție numită 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;
}
Comentarii (7)

Heres un regex soluție care utilizează numai standard regex bibliotecă. (M-am'm un pic ruginit, deci nu pot fi câteva erori de sintaxă, dar cel puțin aceasta este ideea generală)

#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
}
Comentarii (1)

De stringstream poate fi convenabil dacă aveți nevoie pentru a analiza șirului de non-spațiu simboluri:

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, ';')
Comentarii (0)

Până acum am folosit unul în [Boost][1], dar am nevoie de ceva care nu't depinde de el, așa că am ajuns la asta:


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 
Comentarii (0)