it-swarm.it

Come faccio a scorrere le parole di una stringa?

Sto cercando di ripetere le parole di una stringa.

Si può presumere che la stringa sia composta da parole separate da spazi bianchi.

Nota che non mi interessano le funzioni di stringa C o quel tipo di manipolazione/accesso al personaggio. Inoltre, si prega di dare la precedenza all'eleganza rispetto all'efficienza nella propria risposta.

La soluzione migliore che ho adesso è:

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

C'è un modo più elegante per fare questo?

2767
Ashwin Nanjappa

Per quello che vale, ecco un altro modo per estrarre i token da una stringa di input, basandosi solo su strutture di libreria standard. È un esempio della potenza e dell'eleganza che sta dietro al design della 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"));
}

Invece di copiare i token estratti in un flusso di output, è possibile inserirli in un contenitore, utilizzando lo stesso algoritmo copy generico.

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

... o crea direttamente vector:

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

Lo uso per dividere la stringa con un delimitatore. Il primo mette i risultati in un vettore pre-costruito, il secondo restituisce un nuovo vettore.

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

Nota che questa soluzione non salta i token vuoti, quindi il seguente troverà 4 elementi, uno dei quali è vuoto:

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

Una possibile soluzione utilizzando Boost potrebbe essere:

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

Questo approccio potrebbe essere ancora più veloce rispetto all'approccio stringstream. E poiché questa è una funzione di modello generica, può essere utilizzata per suddividere altri tipi di stringhe (wchar, ecc. O UTF-8) utilizzando tutti i tipi di delimitatori.

Vedere la documentazione per i dettagli.

813
ididak
#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;
}
342
kev

Per coloro con i quali non si trova bene a sacrificare tutta l'efficienza per le dimensioni del codice e vedere "efficiente" come un tipo di eleganza, il seguente dovrebbe colpire un punto debole (e penso che la classe contenitore template sia un'aggiunta incredibilmente elegante):

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

Di solito scelgo di utilizzare std::vector<std::string> types come secondo parametro (ContainerT) ... ma list<> è molto più veloce di vector<> per quando l'accesso diretto non è necessario, e puoi persino creare la tua classe di stringhe e usare qualcosa come std::list<subString> dove subString non fa fare qualsiasi copia per aumenti di velocità incredibili.

È più del doppio più veloce del tokenize più veloce su questa pagina e quasi 5 volte più veloce di altri. Inoltre, con i tipi di parametri perfetti è possibile eliminare tutte le stringhe ed elencare le copie per aumenti di velocità aggiuntivi.

Inoltre, non restituisce il risultato (estremamente inefficiente) dei risultati, ma piuttosto trasmette i token come riferimento, consentendo in tal modo di accumulare token utilizzando più chiamate, se lo si desidera.

Infine ti permette di specificare se tagliare i token vuoti dai risultati attraverso un ultimo parametro opzionale.

Tutto ciò di cui ha bisogno è std::string... il resto è facoltativo. Non usa i flussi o la libreria boost, ma è abbastanza flessibile da essere in grado di accettare alcuni di questi tipi stranieri in modo naturale.

176
Marius

Ecco un'altra soluzione. È compatto e abbastanza efficiente:

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

Può facilmente essere templato per gestire separatori di stringhe, stringhe larghe, ecc.

Si noti che la suddivisione di "" risulta in una singola stringa vuota e la suddivisione di "," (cioè sep) produce due stringhe vuote.

Può anche essere facilmente espanso per saltare i token vuoti:

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

Se si desidera dividere una stringa in più delimitatori mentre si ignorano i token vuoti, è possibile utilizzare questa versione:

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;
}
156
Alec Thomas

Questo è il mio modo preferito per scorrere una stringa. Puoi fare tutto ciò che vuoi per Word.

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

istringstream iss(line, istringstream::in);

while( iss >> Word )     
{
    // Do something on `Word` here...
}
113
gnomed

Questo è simile alla domanda Stack Overflow Come faccio a tokenizzare una stringa in 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;
    }
}
79
Ferruccio

Mi piace quanto segue perché mette i risultati in un vettore, supporta una stringa come delim e dà il controllo sulla conservazione dei valori vuoti. Ma non sembra poi buono.

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

Ovviamente, Boost ha un split() che funziona in parte in questo modo. E se per "spazio bianco" intendi davvero qualsiasi tipo di spazio bianco, la divisione di Boost con is_any_of() funziona alla grande.

66
Shadow2531

L'STL non ha già un tale metodo disponibile.

Tuttavia, puoi usare la funzione C strtok() usando il membro std::string::c_str() , oppure puoi scriverne una tua. Ecco un esempio di codice che ho trovato dopo una rapida ricerca su Google ("STL split"):

void Tokenize(const string& str,
              vector<string>& 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);
    }
}

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

Se hai domande sul codice di esempio, lascia un commento e ti spiegherò.

E solo perché non implementa un typedef chiamato iterator o sovraccarico l'operatore << non significa che sia un codice errato. Uso le funzioni C abbastanza frequentemente. Ad esempio, printf e scanf entrambi sono più veloci di std::cin e std::cout (significativamente), fopen syntax è molto più amichevole per i tipi binari, e inoltre tendono a produrre EXE più piccoli.

Non essere venduto su questo "Eleganza over performance" deal.

52
nlaq

Ecco una funzione divisa che:

  • è generico
  • usa standard C++ (senza boost)
  • accetta più delimitatori
  • ignora i token vuoti (può essere facilmente modificato)

    template<typename T>
    vector<T> 
    split(const T & str, const T & delimiters) {
        vector<T> v;
        typename T::size_type start = 0;
        auto pos = str.find_first_of(delimiters, start);
        while(pos != T::npos) {
            if(pos != start) // ignore empty tokens
                v.emplace_back(str, start, pos - start);
            start = pos + 1;
            pos = str.find_first_of(delimiters, start);
        }
        if(start < str.length()) // ignore trailing delimiter
            v.emplace_back(str, start, str.length() - start); // add what's left of the string
        return v;
    }
    

Esempio di utilizzo:

    vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");
40
Marco M.

Ho una soluzione a 2 linee per questo problema:

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

for(size_t p=0, q=0; p!=s.npos; p=q)
  std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;

Quindi invece di stampare puoi metterlo in un vettore.

35
rhomu

Ancora un altro modo flessibile e veloce

template<typename Operator>
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;
  }
}

Per usarlo con un vettore di stringhe (Modifica: Poiché qualcuno ha sottolineato di non ereditare le classi STL ... hrmf;)):

template<class ContainerType>
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<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");

Questo è tutto! E questo è solo un modo per usare il tokenizer, ad esempio come solo Contare le parole:

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

Limitato dalla fantasia;)

35
Robert

Ecco una soluzione semplice che utilizza solo la libreria regex standard

#include <regex>
#include <string>
#include <vector>

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

    std::vector<string> 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;
}

L'argomento regex consente di verificare più argomenti (spazi, virgole, ecc.)

Solitamente controllo solo la divisione su spazi e virgole, quindi ho anche questa funzione predefinita:

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

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

    return Tokenize( str, re );
}

Il "[\\s,]+" controlla gli spazi (\\s) e le virgole (,).

Nota, se vuoi dividere wstring invece di string,

  • cambia tutto std::regex in std::wregex
  • cambia tutto sregex_token_iterator in wsregex_token_iterator

Nota, potresti anche voler prendere l'argomento stringa per riferimento, a seconda del tuo compilatore.

31
dk123

Se si desidera utilizzare boost, ma si desidera utilizzare un'intera stringa come delimitatore (anziché singoli caratteri come nella maggior parte delle soluzioni proposte in precedenza), è possibile utilizzare boost_split_iterator.

Codice di esempio che include un modello conveniente:

#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>

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

    for(It iter=make_split_iterator(str, first_Finder(delim, is_equal()));
            iter!=It();
            ++iter)
    {
        *(result++) = boost::copy_range<std::string>(*iter);
    }
}

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

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

    // or directly to console, for example
    split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
    return 0;
}
25
zerm

Usare std::stringstream come hai funzionato perfettamente e fare esattamente quello che volevi. Se stai solo cercando un modo diverso di fare le cose, puoi usare std::find() / std::find_first_of() e std::string::substr() .

Ecco un esempio:

#include <iostream>
#include <string>

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 << substring << '\n';

        prev_pos = ++pos;
    }

    std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last Word
    std::cout << substring << '\n';

    return 0;
}
25
KTC

Esiste una funzione chiamata strtok .

#include<string>
using namespace std;

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

    vector<string> result;

    while(token != NULL)
    {
        result.Push_back(token);
        token = strtok_r(NULL,delim,&saveptr);
    }
    return result;
}
19
Pratik Deoghare

Ecco una soluzione regex che utilizza solo la libreria standard regex. (Sono un po 'arrugginito, quindi potrebbero esserci alcuni errori di sintassi, ma questa è almeno l'idea generale)

#include <regex.h>
#include <string.h>
#include <vector.h>

using namespace std;

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

Stringstream può essere utile se è necessario analizzare la stringa con simboli non spaziali:

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, ';')
16
lukmac

Finora ho usato quello in Boost , ma avevo bisogno di qualcosa che non dipendesse da esso, quindi sono arrivato a questo:

static void Split(std::vector<std::string>& 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 << input[n];
        else
        {
            if (!Word.str().empty() || !remove_empty)
                lst.Push_back(Word.str());
            Word.str("");
        }
    }
    if (!Word.str().empty() || !remove_empty)
        lst.Push_back(Word.str());
}

Un buon punto è che in separators puoi passare più di un personaggio.

14
Goran

Breve ed elegante

#include <vector>
#include <string>
using namespace std;

vector<string> split(string data, string token)
{
    vector<string> output;
    size_t pos = string::npos; // size_t to avoid improbable overflow
    do
    {
        pos = data.find(token);
        output.Push_back(data.substr(0, pos));
        if (string::npos != pos)
            data = data.substr(pos + token.size());
    } while (string::npos != pos);
    return output;
}

può usare qualsiasi stringa come delimitatore, può anche essere usata con dati binari (std :: string supporta dati binari, compresi i null)

utilizzando:

auto a = split("this!!is!!!example!string", "!!");

produzione:

this
is
!example!string
14
user1438233

Ho lanciato il mio usando strtok e usato boost per dividere una stringa. Il metodo migliore che ho trovato è il C++ String Toolkit Library . È incredibilmente flessibile e veloce.

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

Il toolkit ha molta più flessibilità di questo semplice esempio, ma la sua utilità nell'analizzare una stringa in elementi utili è incredibile.

13
DannyK

L'ho fatto perché avevo bisogno di un modo semplice per dividere stringhe e stringhe basate su c ... Speriamo che qualcun altro possa trovare utile anche questo. Inoltre non si basa su token e puoi usare i campi come delimitatori, che è un'altra chiave di cui avevo bisogno.

Sono sicuro che ci sono miglioramenti che possono essere fatti per migliorare ulteriormente la sua eleganza e per favore fallo con tutti i mezzi

StringSplitter.hpp:

#include <vector>
#include <iostream>
#include <string.h>

using namespace std;

class StringSplit
{
private:
    void copy_fragment(char*, char*, char*);
    void copy_fragment(char*, char*, char);
    bool match_fragment(char*, char*, int);
    int untilnextdelim(char*, char);
    int untilnextdelim(char*, char*);
    void assimilate(char*, char);
    void assimilate(char*, char*);
    bool string_contains(char*, char*);
    long calc_string_size(char*);
    void copy_string(char*, char*);

public:
    vector<char*> split_cstr(char);
    vector<char*> split_cstr(char*);
    vector<string> split_string(char);
    vector<string> split_string(char*);
    char* String;
    bool do_string;
    bool keep_empty;
    vector<char*> Container;
    vector<string> ContainerS;

    StringSplit(char * in)
    {
        String = in;
    }

    StringSplit(string in)
    {
        size_t len = calc_string_size((char*)in.c_str());
        String = new char[len + 1];
        memset(String, 0, len + 1);
        copy_string(String, (char*)in.c_str());
        do_string = true;
    }

    ~StringSplit()
    {
        for (int i = 0; i < Container.size(); i++)
        {
            if (Container[i] != NULL)
            {
                delete[] Container[i];
            }
        }
        if (do_string)
        {
            delete[] String;
        }
    }
};

StringSplitter.cpp:

#include <string.h>
#include <iostream>
#include <vector>
#include "StringSplit.hpp"

using namespace std;

void StringSplit::assimilate(char*src, char delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.Push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.Push_back(x);
            }

        }
        else
        {
            delete[] temp;
        }
    }
}

void StringSplit::assimilate(char*src, char* delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.Push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.Push_back(x);
            }
        }
        else
        {
            delete[] temp;
        }
    }
}

long StringSplit::calc_string_size(char* _in)
{
    long i = 0;
    while (*_in++)
    {
        i++;
    }
    return i;
}

bool StringSplit::string_contains(char* haystack, char* needle)
{
    size_t len = calc_string_size(needle);
    size_t lenh = calc_string_size(haystack);
    while (lenh--)
    {
        if (match_fragment(haystack + lenh, needle, len))
        {
            return true;
        }
    }
    return false;
}

bool StringSplit::match_fragment(char* _src, char* cmp, int len)
{
    while (len--)
    {
        if (*(_src + len) != *(cmp + len))
        {
            return false;
        }
    }
    return true;
}

int StringSplit::untilnextdelim(char* _in, char delim)
{
    size_t len = calc_string_size(_in);
    if (*_in == delim)
    {
        _in += 1;
        return len - 1;
    }

    int c = 0;
    while (*(_in + c) != delim && c < len)
    {
        c++;
    }

    return c;
}

int StringSplit::untilnextdelim(char* _in, char* delim)
{
    int s = calc_string_size(delim);
    int c = 1 + s;

    if (!string_contains(_in, delim))
    {
        return calc_string_size(_in);
    }
    else if (match_fragment(_in, delim, s))
    {
        _in += s;
        return calc_string_size(_in);
    }

    while (!match_fragment(_in + c, delim, s))
    {
        c++;
    }

    return c;
}

void StringSplit::copy_fragment(char* dest, char* src, char delim)
{
    if (*src == delim)
    {
        src++;
    }

    int c = 0;
    while (*(src + c) != delim && *(src + c))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

void StringSplit::copy_string(char* dest, char* src)
{
    int i = 0;
    while (*(src + i))
    {
        *(dest + i) = *(src + i);
        i++;
    }
}

void StringSplit::copy_fragment(char* dest, char* src, char* delim)
{
    size_t len = calc_string_size(delim);
    size_t lens = calc_string_size(src);

    if (match_fragment(src, delim, len))
    {
        src += len;
        lens -= len;
    }

    int c = 0;
    while (!match_fragment(src + c, delim, len) && (c < lens))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

vector<char*> StringSplit::split_cstr(char Delimiter)
{
    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char Delimiter)
{
    do_string = true;

    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

vector<char*> StringSplit::split_cstr(char* Delimiter)
{
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while(*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String,Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char* Delimiter)
{
    do_string = true;
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while (*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

Esempi:

int main(int argc, char*argv[])
{
    StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
    vector<char*> Split = ss.split_cstr(":CUT:");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

Produrrà:

Questo
è
un
esempio
CString

int main(int argc, char*argv[])
{
    StringSplit ss = "This:is:an:example:cstring";
    vector<char*> Split = ss.split_cstr(':');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string("[SPLIT]");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This|is|an|example|string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string('|');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

Per mantenere le voci vuote (per impostazione predefinita saranno esclusi i vuoti):

StringSplit ss = mystring;
ss.keep_empty = true;
vector<string> Split = ss.split_string(":DELIM:");

L'obiettivo era quello di renderlo simile al metodo Split () di C # in cui dividere una stringa è facile come:

String[] Split = 
    "Hey:cut:what's:cut:your:cut:name?".Split(new[]{":cut:"}, StringSplitOptions.None);

foreach(String X in Split)
{
    Console.Write(X);
}

Spero che qualcun altro possa trovarlo utile come me.

11
Steve Dell

Che dire di questo:

#include <string>
#include <vector>

using namespace std;

vector<string> split(string str, const char delim) {
    vector<string> v;
    string tmp;

    for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
        if(*i != delim && i != str.end()) {
            tmp += *i; 
        } else {
            v.Push_back(tmp);
            tmp = ""; 
        }   
    }   

    return v;
}
10
gibbz

Questa risposta prende la stringa e la mette in un vettore di stringhe. Usa la libreria boost.

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

Mi piace utilizzare i metodi boost/regex per questa attività poiché offrono la massima flessibilità per specificare i criteri di suddivisione.

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

int main() {
    std::string line("A:::line::to:split");
    const boost::regex re(":+"); // one or more colons

    // -1 means find inverse matches aka split
    boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
    boost::sregex_token_iterator end;

    for (; tokens != end; ++tokens)
        std::cout << *tokens << std::endl;
}
9
Marty B

Ecco un altro modo di farlo ..

void split_string(string text,vector<string>& words)
{
  int i=0;
  char ch;
  string Word;

  while(ch=text[i++])
  {
    if (isspace(ch))
    {
      if (!Word.empty())
      {
        words.Push_back(Word);
      }
      Word = "";
    }
    else
    {
      Word += ch;
    }
  }
  if (!Word.empty())
  {
    words.Push_back(Word);
  }
}
9
user246110
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;

    vector<string> split(const string &s, char delim) {
        vector<string> elems;
        stringstream ss(s);
        string item;
        while (getline(ss, item, delim)) {
            elems.Push_back(item);
        }
        return elems;
    }

int main() {

        vector<string> x = split("thi is an sample test",' ');
        unsigned int i;
        for(i=0;i<x.size();i++)
            cout<<i<<":"<<x[i]<<endl;
        return 0;
}
9
san45

Recentemente ho dovuto dividere un vocabolo con cammello in sottotitoli. Non ci sono delimitatori, solo caratteri superiori. 

#include <string>
#include <list>
#include <locale> // std::isupper

template<class String>
const std::list<String> split_camel_case_string(const String &s)
{
    std::list<String> R;
    String w;

    for (String::const_iterator i = s.begin(); i < s.end(); ++i) {  {
        if (std::isupper(*i)) {
            if (w.length()) {
                R.Push_back(w);
                w.clear();
            }
        }
        w += *i;
    }

    if (w.length())
        R.Push_back(w);
    return R;
}

Ad esempio, questo divide "AQueryTrades" in "A", "Query" e "Commerci". La funzione funziona con stringhe strette e larghe. Poiché rispetta il locale corrente, divide "RaumfahrtÜberwachungsVerordnung" in "Raumfahrt", "Überwachungs" e "Verordnung".

Nota std::upper dovrebbe essere realmente passato come argomento del modello di funzione. Quindi la più generalizzata di questa funzione può essere divisa in delimitatori come ",", ";" o " ".

9
Andreas Spindler

Il codice seguente utilizza strtok() per dividere una stringa in token e memorizzare i token in un vettore.

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

using namespace std;


char one_line_string[] = "hello hi how are you Nice weather we are having ok then bye";
char seps[]   = " ,\t\n";
char *token;



int main()
{
   vector<string> vec_String_Lines;
   token = strtok( one_line_string, seps );

   cout << "Extracting and storing data in a vector..\n\n\n";

   while( token != NULL )
   {
      vec_String_Lines.Push_back(token);
      token = strtok( NULL, seps );
   }
     cout << "Displaying end result in vector line storage..\n\n";

    for ( int i = 0; i < vec_String_Lines.size(); ++i)
    cout << vec_String_Lines[i] << "\n";
    cout << "\n\n\n";


return 0;
}
8

Ottieni Boost ! : -)

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace boost;

int main(int argc, char**argv) {
    typedef vector < string > list_type;

    list_type list;
    string line;

    line = "Somewhere down the road";
    split(list, line, is_any_of(" "));

    for(int i = 0; i < list.size(); i++)
    {
        cout << list[i] << endl;
    }

    return 0;
}

Questo esempio fornisce l'output - 

Somewhere
down
the
road
8
Aleksey Bykov

Io uso questo semplice perché abbiamo ottenuto la nostra classe String "speciale" (cioè non standard):

void splitString(const String &s, const String &delim, std::vector<String> &result) {
    const int l = delim.length();
    int f = 0;
    int i = s.indexOf(delim,f);
    while (i>=0) {
        String token( i-f > 0 ? s.substring(f,i-f) : "");
        result.Push_back(token);
        f=i+l;
        i = s.indexOf(delim,f);
    }
    String token = s.substring(f);
    result.Push_back(token);
}
7
Abe
#include <iostream>
#include <regex>

using namespace std;

int main() {
   string s = "foo bar  baz";
   regex e("\\s+");
   regex_token_iterator<string::iterator> i(s.begin(), s.end(), e, -1);
   regex_token_iterator<string::iterator> end;
   while (i != end)
      cout << " [" << *i++ << "]";
}

IMO, questa è la cosa più vicina a re.split () di python. Vedi cplusplus.com per ulteriori informazioni su regex_token_iterator. Il -1 (quarto argomento in regex_token_iterator ctor) è la sezione della sequenza che non corrisponde, usando la corrispondenza come separatore.

6
solstice333

Quello che segue è un modo molto migliore per farlo. Può assumere qualsiasi carattere e non dividere le righe a meno che tu non voglia. Non sono necessarie librerie speciali (beh, oltre a std, ma chi la considera davvero una libreria extra), nessun puntatore, nessun riferimento ed è statico. Semplicemente semplice C++.

#pragma once
#include <vector>
#include <sstream>
using namespace std;
class Helpers
{
    public:
        static vector<string> split(string s, char delim)
        {
            stringstream temp (stringstream::in | stringstream::out);
            vector<string> elems(0);
            if (s.size() == 0 || delim == 0)
                return elems;
            for(char c : s)
            {
                if(c == delim)
                {
                    elems.Push_back(temp.str());
                    temp = stringstream(stringstream::in | stringstream::out);
                }
                else
                    temp << c;
            }
            if (temp.str().size() > 0)
                elems.Push_back(temp.str());
                return elems;
            }

        //Splits string s with a list of delimiters in delims (it's just a list, like if we wanted to
        //split at the following letters, a, b, c we would make delims="abc".
        static vector<string> split(string s, string delims)
        {
            stringstream temp (stringstream::in | stringstream::out);
            vector<string> elems(0);
            bool found;
            if(s.size() == 0 || delims.size() == 0)
                return elems;
            for(char c : s)
            {
                found = false;
                for(char d : delims)
                {
                    if (c == d)
                    {
                        elems.Push_back(temp.str());
                        temp = stringstream(stringstream::in | stringstream::out);
                        found = true;
                        break;
                    }
                }
                if(!found)
                    temp << c;
            }
            if(temp.str().size() > 0)
                elems.Push_back(temp.str());
            return elems;
        }
};
6
Kelly Elton

Come hobbista, questa è la prima soluzione che mi è venuta in mente. Sono curioso di sapere perché non ho ancora visto una soluzione simile qui, c'è qualcosa di fondamentalmente sbagliato in come l'ho fatto?

#include <iostream>
#include <string>
#include <vector>

std::vector<std::string> split(const std::string &s, const std::string &delims)
{
    std::vector<std::string> result;
    std::string::size_type pos = 0;
    while (std::string::npos != (pos = s.find_first_not_of(delims, pos))) {
        auto pos2 = s.find_first_of(delims, pos);
        result.emplace_back(s.substr(pos, std::string::npos == pos2 ? pos2 : pos2 - pos));
        pos = pos2;
    }
    return result;
}

int main()
{
    std::string text{"And then I said: \"I don't get it, why would you even do that!?\""};
    std::string delims{" :;\".,?!"};
    auto words = split(text, delims);
    std::cout << "\nSentence:\n  " << text << "\n\nWords:";
    for (const auto &w : words) {
        std::cout << "\n  " << w;
    }
    return 0;
}

http://cpp.sh/7wmzy

5
Jehjoa

Ho scritto il seguente pezzo di codice. È possibile specificare il delimitatore, che può essere una stringa . Il risultato è simile a String.split di Java, con una stringa vuota nel risultato.

Ad esempio, se chiamiamo split ("ABCPICKABCANYABCTWO: ABC", "ABC"), il risultato è il seguente:

0  <len:0>
1 PICK <len:4>
2 ANY <len:3>
3 TWO: <len:4>
4  <len:0>

Codice:

vector <string> split(const string& str, const string& delimiter = " ") {
    vector <string> tokens;

    string::size_type lastPos = 0;
    string::size_type pos = str.find(delimiter, lastPos);

    while (string::npos != pos) {
        // Found a token, add it to the vector.
        cout << str.substr(lastPos, pos - lastPos) << endl;
        tokens.Push_back(str.substr(lastPos, pos - lastPos));
        lastPos = pos + delimiter.size();
        pos = str.find(delimiter, lastPos);
    }

    tokens.Push_back(str.substr(lastPos, str.size() - lastPos));
    return tokens;
}
5
Jim Huang

Quando si ha a che fare con spazi bianchi come separatori, la risposta ovvia all'uso di std::istream_iterator<T> è già stata data e votata molto. Ovviamente, gli elementi non possono essere separati da spazi bianchi ma da un separatore. Non ho individuato alcuna risposta che ridefinisce il significato di spazio bianco per essere detto separatore e quindi utilizza l'approccio convenzionale.

Il modo per cambiare ciò che gli stream considerano spazi bianchi, devi semplicemente cambiare lo std::locale del flusso usando (std::istream::imbue()) con un faccetto std::ctype<char> con la sua definizione di cosa vuol dire spazio (può essere fatto anche per std::ctype<wchar_t>, ma in realtà è leggermente diverso perché std::ctype<char> è basato su tabelle mentre std::ctype<wchar_t> è guidato da funzioni virtuali).

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

struct whitespace_mask {
    std::ctype_base::mask mask_table[std::ctype<char>::table_size];
    whitespace_mask(std::string const& spaces) {
        std::ctype_base::mask* table = this->mask_table;
        std::ctype_base::mask const* tab
            = std::use_facet<std::ctype<char>>(std::locale()).table();
        for (std::size_t i(0); i != std::ctype<char>::table_size; ++i) {
            table[i] = tab[i] & ~std::ctype_base::space;
        }
        std::for_each(spaces.begin(), spaces.end(), [=](unsigned char c) {
            table[c] |= std::ctype_base::space;
        });
    }
};
class whitespace_facet
    : private whitespace_mask
    , public std::ctype<char> {
public:
    whitespace_facet(std::string const& spaces)
        : whitespace_mask(spaces)
        , std::ctype<char>(this->mask_table) {
    }
};

struct whitespace {
    std::string spaces;
    whitespace(std::string const& spaces): spaces(spaces) {}
};
std::istream& operator>>(std::istream& in, whitespace const& ws) {
    std::locale loc(in.getloc(), new whitespace_facet(ws.spaces));
    in.imbue(loc);
    return in;
}
// everything above would probably go into a utility library...

int main() {
    std::istringstream in("a, b, c, d, e");
    std::copy(std::istream_iterator<std::string>(in >> whitespace(", ")),
              std::istream_iterator<std::string>(),
              std::ostream_iterator<std::string>(std::cout, "\n"));

    std::istringstream pipes("a b c|  d |e     e");
    std::copy(std::istream_iterator<std::string>(pipes >> whitespace("|")),
              std::istream_iterator<std::string>(),
              std::ostream_iterator<std::string>(std::cout, "\n"));   
}

La maggior parte del codice è per il confezionamento di uno strumento generico che fornisce delimitatori soft : si uniscono più delimitatori in una riga. Non c'è modo di produrre una sequenza vuota. Quando sono necessari diversi delimitatori all'interno di un flusso, probabilmente utilizzerai flussi di configurazione diversi utilizzando un buffer di flusso condiviso:

void f(std::istream& in) {
    std::istream pipes(in.rdbuf());
    pipes >> whitespace("|");
    std::istream comma(in.rdbuf());
    comma >> whitespace(",");

    std::string s0, s1;
    if (pipes >> s0 >> std::ws   // read up to first pipe and ignore sequence of pipes
        && comma >> s1 >> std::ws) { // read up to first comma and ignore commas
        // ...
    }
}
5
Dietmar Kühl

Ecco la mia soluzione usando C++ 11 e il STL. Dovrebbe essere ragionevolmente efficiente:

#include <vector>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>

std::vector<std::string> split(const std::string& s)
{
    std::vector<std::string> v;

    const auto end = s.end();
    auto to = s.begin();
    decltype(to) from;

    while((from = std::find_if(to, end,
        [](char c){ return !std::isspace(c); })) != end)
    {
        to = std::find_if(from, end, [](char c){ return std::isspace(c); });
        v.emplace_back(from, to);
    }

    return v;
}

int main()
{
    std::string s = "this is the string  to  split";

    auto v = split(s);

    for(auto&& s: v)
        std::cout << s << '\n';
}

Produzione:

this
is
the
string
to
split
4
Galik

Questa è la mia versione presa dalla fonte di Kev:

#include <string>
#include <vector>
void split(vector<string> &result, string str, char delim ) {
  string tmp;
  string::iterator i;
  result.clear();

  for(i = str.begin(); i <= str.end(); ++i) {
    if((const char)*i != delim  && i != str.end()) {
      tmp += *i;
    } else {
      result.Push_back(tmp);
      tmp = "";
    }
  }
}

Dopo, chiama la funzione e fai qualcosa con esso:

vector<string> hosts;
split(hosts, "192.168.1.2,192.168.1.3", ',');
for( size_t i = 0; i < hosts.size(); i++){
  cout <<  "Connecting Host : " << hosts.at(i) << "..." << endl;
}

Io uso il seguente codice:

namespace Core
{
    typedef std::wstring String;

    void SplitString(const Core::String& input, const Core::String& splitter, std::list<Core::String>& output)
    {
        if (splitter.empty())
        {
            throw std::invalid_argument(); // for example
        }

        std::list<Core::String> lines;

        Core::String::size_type offset = 0;

        for (;;)
        {
            Core::String::size_type splitterPos = input.find(splitter, offset);

            if (splitterPos != Core::String::npos)
            {
                lines.Push_back(input.substr(offset, splitterPos - offset));
                offset = splitterPos + splitter.size();
            }
            else
            {
                lines.Push_back(input.substr(offset));
                break;
            }
        }

        lines.swap(output);
    }
}

// gtest:

class SplitStringTest: public testing::Test
{
};

TEST_F(SplitStringTest, EmptyStringAndSplitter)
{
    std::list<Core::String> result;
    ASSERT_ANY_THROW(Core::SplitString(Core::String(), Core::String(), result));
}

TEST_F(SplitStringTest, NonEmptyStringAndEmptySplitter)
{
    std::list<Core::String> result;
    ASSERT_ANY_THROW(Core::SplitString(L"xy", Core::String(), result));
}

TEST_F(SplitStringTest, EmptyStringAndNonEmptySplitter)
{
    std::list<Core::String> result;
    Core::SplitString(Core::String(), Core::String(L","), result);
    ASSERT_EQ(1, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
}

TEST_F(SplitStringTest, OneCharSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L"x,y", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y", *result.rbegin());

    Core::SplitString(L",xy", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L"xy", *result.rbegin());

    Core::SplitString(L"xy,", L",", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"xy", *result.begin());
    ASSERT_EQ(Core::String(), *result.rbegin());
}

TEST_F(SplitStringTest, TwoCharsSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L"x,.y,z", L",.", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y,z", *result.rbegin());

    Core::SplitString(L"x,,y,z", L",,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y,z", *result.rbegin());
}

TEST_F(SplitStringTest, RecursiveSplitter)
{
    std::list<Core::String> result;

    Core::SplitString(L",,,", L",,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L",", *result.rbegin());

    Core::SplitString(L",.,.,", L",.,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(L".,", *result.rbegin());

    Core::SplitString(L"x,.,.,y", L",.,", result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L".,y", *result.rbegin());

    Core::SplitString(L",.,,.,", L",.,", result);
    ASSERT_EQ(3, result.size());
    ASSERT_EQ(Core::String(), *result.begin());
    ASSERT_EQ(Core::String(), *(++result.begin()));
    ASSERT_EQ(Core::String(), *result.rbegin());
}

TEST_F(SplitStringTest, NullTerminators)
{
    std::list<Core::String> result;

    Core::SplitString(L"xy", Core::String(L"\0", 1), result);
    ASSERT_EQ(1, result.size());
    ASSERT_EQ(L"xy", *result.begin());

    Core::SplitString(Core::String(L"x\0y", 3), Core::String(L"\0", 1), result);
    ASSERT_EQ(2, result.size());
    ASSERT_EQ(L"x", *result.begin());
    ASSERT_EQ(L"y", *result.rbegin());
}
4
Dmitry

Versione rapida che usa vector come classe base, dando pieno accesso a tutti i suoi operatori:

    // Split string into parts.
    class Split : public std::vector<std::string>
    {
        public:
            Split(const std::string& str, char* delimList)
            {
               size_t lastPos = 0;
               size_t pos = str.find_first_of(delimList);

               while (pos != std::string::npos)
               {
                    if (pos != lastPos)
                        Push_back(str.substr(lastPos, pos-lastPos));
                    lastPos = pos + 1;
                    pos = str.find_first_of(delimList, lastPos);
               }
               if (lastPos < str.length())
                   Push_back(str.substr(lastPos, pos-lastPos));
            }
    };

Esempio utilizzato per popolare un set STL:

std::set<std::string> words;
Split split("Hello,World", ",");
words.insert(split.begin(), split.end());
3
landen

No Boost, nessun flusso di stringhe, solo la libreria C standard che collabora con std::string e std::list: funzioni di libreria C per un'analisi semplice, tipi di dati C++ per una facile gestione della memoria.

Lo spazio bianco è considerato come una qualsiasi combinazione di nuove righe, tabulazioni e spazi. L'insieme di caratteri di spaziatura è stabilito dalla variabile wschars.

#include <string>
#include <list>
#include <iostream>
#include <cstring>

using namespace std;

const char *wschars = "\t\n ";

list<string> split(const string &str)
{
  const char *cstr = str.c_str();
  list<string> out;

  while (*cstr) {                     // while remaining string not empty
    size_t toklen;
    cstr += strspn(cstr, wschars);    // skip leading whitespace
    toklen = strcspn(cstr, wschars);  // figure out token length
    if (toklen)                       // if we have a token, add to list
      out.Push_back(string(cstr, toklen));
    cstr += toklen;                   // skip over token
  }

  // ran out of string; return list

  return out;
}

int main(int argc, char **argv)
{
  list<string> li = split(argv[1]);
  for (list<string>::iterator i = li.begin(); i != li.end(); i++)
    cout << "{" << *i << "}" << endl;
  return 0;
}

Correre:

$ ./split ""
$ ./split "a"
{a}
$ ./split " a "
{a}
$ ./split " a b"
{a}
{b}
$ ./split " a b c"
{a}
{b}
{c}
$ ./split " a b c d  "
{a}
{b}
{c}
{d}

Versione ricorsiva di split (divisa in due funzioni). Tutte le manipolazioni distruttive delle variabili sono sparite, tranne che per la spinta delle stringhe nella lista!

void split_rec(const char *cstr, list<string> &li)
{
  if (*cstr) {
    const size_t leadsp = strspn(cstr, wschars);
    const size_t toklen = strcspn(cstr + leadsp, wschars);

    if (toklen)
      li.Push_back(string(cstr + leadsp, toklen));

    split_rec(cstr + leadsp + toklen, li);
  }
}

list<string> split(const string &str)
{
  list<string> out;
  split_rec(str.c_str(), out);
  return out;
}
2
Kaz

Sì, ho esaminato tutti e 30 gli esempi.

Non sono riuscito a trovare una versione di split che funzioni per delimitatori multi-char, quindi ecco la mia: 

#include <string>
#include <vector>

using namespace std;

vector<string> split(const string &str, const string &delim)
{   
    const auto delim_pos = str.find(delim);

    if (delim_pos == string::npos)
        return {str};

    vector<string> ret{str.substr(0, delim_pos)};
    auto tail = split(str.substr(delim_pos + delim.size(), string::npos), delim);

    ret.insert(ret.end(), tail.begin(), tail.end());

    return ret;
}

Probabilmente non è la più efficiente delle implementazioni, ma è una soluzione ricorsiva molto semplice, che usa solo <string> e <vector>.

Ah, è scritto in C++ 11, ma non c'è niente di speciale in questo codice, quindi puoi facilmente adattarlo a C++ 98.

2
Romário

LazyStringSplitter:

#include <string>
#include <algorithm>
#include <unordered_set>

using namespace std;

class LazyStringSplitter
{
    string::const_iterator start, finish;
    unordered_set<char> chop;

public:

    // Empty Constructor
    explicit LazyStringSplitter()
    {}

    explicit LazyStringSplitter (const string cstr, const string delims)
        : start(cstr.begin())
        , finish(cstr.end())
        , chop(delims.begin(), delims.end())
    {}

    void operator () (const string cstr, const string delims)
    {
        chop.insert(delims.begin(), delims.end());
        start = cstr.begin();
        finish = cstr.end();
    }

    bool empty() const { return (start >= finish); }

    string next()
    {
        // return empty string
        // if ran out of characters
        if (empty())
            return string("");

        auto runner = find_if(start, finish, [&](char c) {
            return chop.count(c) == 1;
        });

        // construct next string
        string ret(start, runner);
        start = runner + 1;

        // Never return empty string
        // + tail recursion makes this method efficient
        return !ret.empty() ? ret : next();
    }
};
  • Io chiamo questo metodo LazyStringSplitter per una ragione - Non divide la stringa in un colpo solo.
  • In sostanza si comporta come un generatore di python
  • Espone un metodo chiamato next che restituisce la stringa successiva che è divisa dall'originale
  • Ho fatto uso di unordered_set di c ++ 11 STL, in modo che la ricerca dei delimitatori sia molto più veloce
  • Ed ecco come funziona

TEST PROGRAM

#include <iostream>
using namespace std;

int main()
{
    LazyStringSplitter splitter;

    // split at the characters ' ', '!', '.', ','
    splitter("This, is a string. And here is another string! Let's test and see how well this does.", " !.,");

    while (!splitter.empty())
        cout << splitter.next() << endl;
    return 0;
}

PRODUZIONE

This
is
a
string
And
here
is
another
string
Let's
test
and
see
how
well
this
does

Il prossimo piano per migliorarlo consiste nell'implementare i metodi begin e end in modo che si possa fare qualcosa del tipo:

vector<string> split_string(splitter.begin(), splitter.end());
2
smac89

Per coloro che hanno bisogno di alternative nella suddivisione della stringa con un delimitatore di stringhe, forse puoi provare la mia soluzione seguente.

std::vector<size_t> str_pos(const std::string &search, const std::string &target)
{
    std::vector<size_t> founds;

    if(!search.empty())
    {
        size_t start_pos = 0;

        while (true)
        {
            size_t found_pos = target.find(search, start_pos);

            if(found_pos != std::string::npos)
            {
                size_t found = found_pos;

                founds.Push_back(found);

                start_pos = (found_pos + 1);
            }
            else
            {
                break;
            }
        }
    }

    return founds;
}

std::string str_sub_index(size_t begin_index, size_t end_index, const std::string &target)
{
    std::string sub;

    size_t size = target.length();

    const char* copy = target.c_str();

    for(size_t i = begin_index; i <= end_index; i++)
    {
        if(i >= size)
        {
            break;
        }
        else
        {
            char c = copy[i];

            sub += c;
        }
    }

    return sub;
}

std::vector<std::string> str_split(const std::string &delimiter, const std::string &target)
{
    std::vector<std::string> splits;

    if(!delimiter.empty())
    {
        std::vector<size_t> founds = str_pos(delimiter, target);

        size_t founds_size = founds.size();

        if(founds_size > 0)
        {
            size_t search_len = delimiter.length();

            size_t begin_index = 0;

            for(int i = 0; i <= founds_size; i++)
            {
                std::string sub;

                if(i != founds_size)
                {
                    size_t pos  = founds.at(i);

                    sub = str_sub_index(begin_index, pos - 1, target);

                    begin_index = (pos + search_len);
                }
                else
                {
                    sub = str_sub_index(begin_index, (target.length() - 1), target);
                }

                splits.Push_back(sub);
            }
        }
    }

    return splits;
}

Questi frammenti consistono in 3 funzioni. La cattiva notizia è usare la funzione str_split che ti servirà le altre due funzioni. Sì, è un enorme pezzo di codice. Ma la buona notizia è che queste due funzioni aggiuntive sono in grado di funzionare indipendentemente e talvolta possono essere anche utili .. :)

Testato la funzione nel blocco main() in questo modo:

int main()
{
    std::string s = "Hello, world! We need to make the world a better place. Because your world is also my world, and our children's world.";

    std::vector<std::string> split = str_split("world", s);

    for(int i = 0; i < split.size(); i++)
    {
        std::cout << split[i] << std::endl;
    }
}

E produrrebbe:

Hello, 
! We need to make the 
 a better place. Because your 
 is also my 
, and our children's 
.

Credo che non sia il codice più efficiente, ma almeno funziona. Spero che sia d'aiuto.

2
yunhasnawa

Io uso il seguente

void split(string in, vector<string>& parts, char separator) {
    string::iterator  ts, curr;
    ts = curr = in.begin();
    for(; curr <= in.end(); curr++ ) {
        if( (curr == in.end() || *curr == separator) && curr > ts )
               parts.Push_back( string( ts, curr ));
        if( curr == in.end() )
               break;
        if( *curr == separator ) ts = curr + 1; 
    }
}

PlasmaHH, ho dimenticato di includere il controllo extra (curr> ts) per rimuovere i token con spazi bianchi. 

2
ManiP

Questa è la mia soluzione a questo problema:

vector<string> get_tokens(string str) {
    vector<string> dt;
    stringstream ss;
    string tmp; 
    ss << str;
    for (size_t i; !ss.eof(); ++i) {
        ss >> tmp;
        dt.Push_back(tmp);
    }
    return dt;
}

Questa funzione restituisce un vettore di stringhe.

2
pz64_

Ecco la mia versione

#include <vector>

inline std::vector<std::string> Split(const std::string &str, const std::string &delim = " ")
{
    std::vector<std::string> tokens;
    if (str.size() > 0)
    {
        if (delim.size() > 0)
        {
            std::string::size_type currPos = 0, prevPos = 0;
            while ((currPos = str.find(delim, prevPos)) != std::string::npos)
            {
                std::string item = str.substr(prevPos, currPos - prevPos);
                if (item.size() > 0)
                {
                    tokens.Push_back(item);
                }
                prevPos = currPos + 1;
            }
            tokens.Push_back(str.substr(prevPos));
        }
        else
        {
            tokens.Push_back(str);
        }
    }
    return tokens;
}

Funziona con delimitatori a più caratteri. Impedisce ai token vuoti di ottenere i risultati. Usa una singola intestazione. Restituisce la stringa come un singolo token quando non si fornisce alcun delimitatore. Restituisce anche un risultato vuoto se la stringa è vuota. È sfortunatamente inefficiente a causa dell'enorme std::vector copiaA MENO CHEnon si stia compilando usando C++ 11, che dovrebbe usare lo schema di spostamento. In C++ 11, questo codice dovrebbe essere veloce.

2
mchiasson

Ho cercato un modo per dividere una stringa da un separatore di qualsiasi lunghezza, così ho iniziato a scriverlo da zero, poiché le soluzioni esistenti non mi andavano bene.

Ecco il mio piccolo algoritmo, usando solo STL:

//use like this
//std::vector<std::wstring> vec = Split<std::wstring> (L"Hello##world##!", L"##");

template <typename valueType>
static std::vector <valueType> Split (valueType text, const valueType& delimiter)
{
    std::vector <valueType> tokens;
    size_t pos = 0;
    valueType token;

    while ((pos = text.find(delimiter)) != valueType::npos) 
    {
        token = text.substr(0, pos);
        tokens.Push_back (token);
        text.erase(0, pos + delimiter.length());
    }
    tokens.Push_back (text);

    return tokens;
}

Può essere usato con separatori di qualsiasi lunghezza e forma, per quanto ho provato .. _. Istanziare con un tipo stringa o stringa.

Tutto ciò che fa l'algoritmo è la ricerca del delimitatore, ottiene la parte della stringa fino al delimitatore, cancella il delimitatore e ricomincia a cercare finché non lo trova più.

Naturalmente, puoi usare qualsiasi numero di spazi bianchi per il delimitatore.

Spero possa essere d'aiuto.

2
robcsi

Questa è una funzione che ho scritto che mi aiuta a fare molto. Mi ha aiutato quando si eseguiva il protocollo per WebSockets

using namespace std;
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

vector<string> split ( string input , string split_id ) {
  vector<string> result;
  int i = 0;
  bool add;
  string temp;
  stringstream ss;
  size_t found;
  string real;
  int r = 0;
    while ( i != input.length() ) {
        add = false;
        ss << input.at(i);
        temp = ss.str();
        found = temp.find(split_id);
        if ( found != string::npos ) {
            add = true;
            real.append ( temp , 0 , found );
        } else if ( r > 0 &&  ( i+1 ) == input.length() ) {
            add = true;
            real.append ( temp , 0 , found );
        }
        if ( add ) {
            result.Push_back(real);
            ss.str(string());
            ss.clear();
            temp.clear();
            real.clear();
            r = 0;
        }
        i++;
        r++;
    }
  return result;
}

int main() {
    string s = "S,o,m,e,w,h,e,r,e, down the road \n In a really big C++ house.  \n  Lives a little old lady.   \n   That no one ever knew.    \n    She comes outside.     \n     In the very hot Sun.      \n\n\n\n\n\n\n\n   And throws C++ at us.    \n    The End.  FIN.";
    vector < string > Token;
    Token = split ( s , "," );
    for ( int i = 0 ; i < Token.size(); i++)    cout << Token.at(i) << endl;
    cout << endl << Token.size();
    int a;
    cin >> a;
    return a;
}
2
User

Ecco la mia voce:

template <typename Container, typename InputIter, typename ForwardIter>
Container
split(InputIter first, InputIter last,
      ForwardIter s_first, ForwardIter s_last)
{
    Container output;

    while (true) {
        auto pos = std::find_first_of(first, last, s_first, s_last);
        output.emplace_back(first, pos);
        if (pos == last) {
            break;
        }

        first = ++pos;
    }

    return output;
}

template <typename Output = std::vector<std::string>,
          typename Input = std::string,
          typename Delims = std::string>
Output
split(const Input& input, const Delims& delims = " ")
{
    using std::cbegin;
    using std::cend;
    return split<Output>(cbegin(input), cend(input),
                         cbegin(delims), cend(delims));
}

auto vec = split("Mary had a little lamb");

La prima definizione è una funzione generica in stile STL che utilizza due coppie di iteratori. Il secondo è una funzione di convenienza per evitare di dover fare tutti i begin()s e end()s da soli. È anche possibile specificare il tipo di contenitore di output come parametro del modello se si desidera utilizzare un list, ad esempio.

Ciò che lo rende elegante (IMO) è che a differenza della maggior parte delle altre risposte, non è limitato alle stringhe ma funziona con qualsiasi contenitore compatibile con STL. Senza alcuna modifica al codice sopra, puoi dire:

using vec_of_vecs_t = std::vector<std::vector<int>>;

std::vector<int> v{1, 2, 0, 3, 4, 5, 0, 7, 8, 0, 9};
auto r = split<vec_of_vecs_t>(v, std::initializer_list<int>{0, 2});

che dividerà il vettore v in vettori separati ogni volta che si incontra 0 o 2.

(C'è anche il bonus aggiuntivo che con le stringhe, questa implementazione è più veloce di entrambe le versioni strtok()- e getline()-, almeno sul mio sistema.)

1
Tristan Brindle

Solo per comodità:

template<class V, typename T>
bool in(const V &v, const T &el) {
    return std::find(v.begin(), v.end(), el) != v.end();
}

La suddivisione effettiva basata su più delimitatori:

std::vector<std::string> split(const std::string &s,
                               const std::vector<char> &delims) {
    std::vector<std::string> res;
    auto stuff = [&delims](char c) { return !in(delims, c); };
    auto space = [&delims](char c) { return in(delims, c); };
    auto first = std::find_if(s.begin(), s.end(), stuff);
    while (first != s.end()) {
        auto last = std::find_if(first, s.end(), space);
        res.Push_back(std::string(first, last));
        first = std::find_if(last + 1, s.end(), stuff);
    }
    return res;
}

L'utilizzo:

int main() {
    std::string s = "   aaa,  bb  cc ";
    for (auto el: split(s, {' ', ','}))
        std::cout << el << std::endl;
    return 0;
}
1
AlwaysLearning
// adapted from a "regular" csv parse
std::string stringIn = "my csv  is 10233478 NOTseparated by commas";
std::vector<std::string> commaSeparated(1);
int commaCounter = 0;
for (int i=0; i<stringIn.size(); i++) {
    if (stringIn[i] == " ") {
        commaSeparated.Push_back("");
        commaCounter++;
    } else {
        commaSeparated.at(commaCounter) += stringIn[i];
    }
}

alla fine avrai un vettore di stringhe con ogni elemento della frase separato da spazi. solo la risorsa non standard è std :: vector (ma dal momento che è implicata una stringa std :: ho pensato che sarebbe accettabile).

le stringhe vuote vengono salvate come elementi separati.

1
tony gil

Credo che nessuno abbia ancora pubblicato questa soluzione. Invece di usare i delimitatori direttamente, fondamentalmente fa lo stesso di boost :: split (), cioè consente di passare un predicato che restituisce true se un char è un delimitatore e false altrimenti. Penso che questo dia al programmatore un controllo molto maggiore, e la cosa bella è che non hai bisogno di aumentare.

template <class Container, class String, class Predicate>
void split(Container& output, const String& input,
           const Predicate& pred, bool trimEmpty = false) {
    auto it = begin(input);
    auto itLast = it;
    while (it = find_if(it, end(input), pred), it != end(input)) {
        if (not (trimEmpty and it == itLast)) {
            output.emplace_back(itLast, it);
        }
        ++it;
        itLast = it;
    }
}

Quindi puoi usarlo in questo modo:

struct Delim {
    bool operator()(char c) {
        return not isalpha(c);
    }
};    

int main() {
    string s("#include<iostream>\n"
             "int main() { std::cout << \"Hello world!\" << std::endl; }");

    vector<string> v;

    split(v, s, Delim(), true);
    /* Which is also the same as */
    split(v, s, [](char c) { return not isalpha(c); }, true);

    for (const auto& i : v) {
        cout << i << endl;
    }
}
1
LLLL

Il mio codice è:

#include <list>
#include <string>
template<class StringType = std::string, class ContainerType = std::list<StringType> >
class DSplitString:public ContainerType
{
public:
    explicit DSplitString(const StringType& strString, char cChar, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(cChar, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                Push_back(strTemp);
            iPos = iPos_char + 1;
        }
    }
    explicit DSplitString(const StringType& strString, const StringType& strSub, bool bSkipEmptyParts = true)
    {
        size_t iPos = 0;
        size_t iPos_char = 0;
        while(StringType::npos != (iPos_char = strString.find(strSub, iPos)))
        {
            StringType strTemp = strString.substr(iPos, iPos_char - iPos);
            if((bSkipEmptyParts && !strTemp.empty()) || (!bSkipEmptyParts))
                Push_back(strTemp);
            iPos = iPos_char + strSub.length();
        }
    }
};

Esempio:

#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
    DSplitString<> aa("doicanhden1;doicanhden2;doicanhden3;", ';');
    for each (std::string var in aa)
    {
        std::cout << var << std::endl;
    }
    std::cin.get();
    return 0;
}
1
doicanhden

Possiamo usare strtok in c ++,

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char str[]="Mickey M;12034;911416313;M;01a;9001;NULL;0;13;12;0;CPP,C;MSC,3D;FEND,BEND,SEC;";
    char *pch = strtok (str,";,");
    while (pch != NULL)
    {
        cout<<pch<<"\n";
        pch = strtok (NULL, ";,");
    }
    return 0;
}
1
Venkata Naidu M
#include <iostream>
#include <vector>
using namespace std;

int main() {
  string str = "ABC AABCD CDDD RABC GHTTYU FR";
  str += " "; //dirty hack: adding extra space to the end
  vector<string> v;

  for (int i=0; i<(int)str.size(); i++) {
    int a, b;
    a = i;

    for (int j=i; j<(int)str.size(); j++) {
      if (str[j] == ' ') {
        b = j;
        i = j;
        break;
      }
    }
    v.Push_back(str.substr(a, b-a));
  }

  for (int i=0; i<v.size(); i++) {
    cout<<v[i].size()<<" "<<v[i]<<endl;
  }
  return 0;
}
1
torayeff

Effettua il loop su getline con '' come token.

1
lemic

Ecco la mia opinione su questo. Ho dovuto elaborare la stringa di input Word per Word, che avrebbe potuto essere eseguita utilizzando lo spazio per contare le parole, ma ho ritenuto che sarebbe stato noioso e avrei dovuto dividere le parole in vettori. 

#include<iostream>
#include<vector>
#include<string>
#include<stdio.h>
using namespace std;
int main()
{
    char x = '\0';
    string s = "";
    vector<string> q;
    x = getchar();
    while(x != '\n')
    {
        if(x == ' ')
        {
            q.Push_back(s);
            s = "";
            x = getchar();
            continue;
        }
        s = s + x;
        x = getchar();
    }
    q.Push_back(s);
    for(int i = 0; i<q.size(); i++)
        cout<<q[i]<<" ";
    return 0;
}
  1. Non si prende cura di più spazi.
  2. Se l'ultima parola non è immediatamente seguita dal carattere di fine riga, include lo spazio bianco tra l'ultimo carattere di ultima parola e il carattere di fine riga.
1
Saksham Sharma

Ho appena scritto un bell'esempio di come dividere un carattere per simbolo, che quindi posiziona ogni matrice di caratteri (parole separate dal simbolo) in un vettore. Per semplicità ho creato il tipo vettoriale di stringa std.

Spero che questo aiuti ed è leggibile per voi.

#include <vector>
#include <string>
#include <iostream>

void Push(std::vector<std::string> &WORDS, std::string &TMP){
    WORDS.Push_back(TMP);
    TMP = "";
}
std::vector<std::string> mySplit(char STRING[]){
        std::vector<std::string> words;
        std::string s;
        for(unsigned short i = 0; i < strlen(STRING); i++){
            if(STRING[i] != ' '){
                s += STRING[i];
            }else{
                Push(words, s);
            }
        }
        Push(words, s);//Used to get last split
        return words;
}

int main(){
    char string[] = "My awesome string.";
    std::cout << mySplit(string)[2];
    std::cin.get();
    return 0;
}
1
user2588062

Basato su La risposta di Galik L'ho fatto. Questo è principalmente qui, quindi non devo continuare a scriverlo ancora e ancora. È assurdo che C++ non abbia ancora una funzione di divisione nativa. Caratteristiche:

  • Dovrebbe essere molto veloce.
  • Facile da capire (penso).
  • Unisce sezioni vuote.
  • Trivial per utilizzare diversi delimitatori (ad esempio "\r\n")
#include <string>
#include <vector>
#include <algorithm>

std::vector<std::string> split(const std::string& s, const std::string& delims)
{
    using namespace std;

    vector<string> v;

    // Start of an element.
    size_t elemStart = 0;

    // We start searching from the end of the previous element, which
    // initially is the start of the string.
    size_t elemEnd = 0;

    // Find the first non-delim, i.e. the start of an element, after the end of the previous element.
    while((elemStart = s.find_first_not_of(delims, elemEnd)) != string::npos)
    {
        // Find the first delem, i.e. the end of the element (or if this fails it is the end of the string).
        elemEnd = s.find_first_of(delims, elemStart);
        // Add it.
        v.emplace_back(s, elemStart, elemEnd == string::npos ? string::npos : elemEnd - elemStart);
    }
    // When there are no more non-spaces, we are done.

    return v;
}
1
Timmmm

Utilizzando std::string_view e la libreria range-v3 di Eric Niebler:

https://wandbox.org/permlink/kW5lwRCL1pxjp2pW

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"

int main() {
    std::string s = "Somewhere down the range v3 library";
    ranges::for_each(s  
        |   ranges::view::split(' ')
        |   ranges::view::transform([](auto &&sub) {
                return std::string_view(&*sub.begin(), ranges::distance(sub));
            }),
        [](auto s) {std::cout << "Substring: " << s << "\n";}
    );
}
1
Porsche9II

Ho un approccio molto diverso dalle altre soluzioni che offre molto valore in modi che le altre soluzioni mancano in vari modi, ma naturalmente ha anche i suoi lati negativi. Qui è l'implementazione funzionante, con l'esempio di mettere <tag></tag> attorno alle parole.

Per cominciare, questo problema può essere risolto con un ciclo, senza memoria aggiuntiva e considerando solo quattro casi logici. Concettualmente, siamo interessati ai confini. Il nostro codice dovrebbe riflettere questo: andiamo a scorrere la stringa e guardiamo due caratteri alla volta, tenendo presente che abbiamo casi speciali all'inizio e alla fine della stringa.

Il rovescio della medaglia è che dobbiamo scrivere l'implementazione, che è alquanto dettagliata, ma soprattutto comoda.

Il lato positivo è che abbiamo scritto l'implementazione, quindi è molto facile personalizzarlo in base a esigenze specifiche, come distinguere i confini di sinistra e scrittura, utilizzare qualsiasi set di delimitatori o gestire altri casi come posizioni non limitate o errate.

using namespace std;

#include <iostream>
#include <string>

#include <cctype>

typedef enum boundary_type_e {
    E_BOUNDARY_TYPE_ERROR = -1,
    E_BOUNDARY_TYPE_NONE,
    E_BOUNDARY_TYPE_LEFT,
    E_BOUNDARY_TYPE_RIGHT,
} boundary_type_t;

typedef struct boundary_s {
    boundary_type_t type;
    int pos;
} boundary_t;

bool is_delim_char(int c) {
    return isspace(c); // also compare against any other chars you want to use as delimiters
}

bool is_Word_char(int c) {
    return ' ' <= c && c <= '~' && !is_delim_char(c);
}

boundary_t maybe_Word_boundary(string str, int pos) {
    int len = str.length();
    if (pos < 0 || pos >= len) {
        return (boundary_t){.type = E_BOUNDARY_TYPE_ERROR};
    } else {
        if (pos == 0 && is_Word_char(str[pos])) {
            // if the first character is Word-y, we have a left boundary at the beginning
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos};
        } else if (pos == len - 1 && is_Word_char(str[pos])) {
            // if the last character is Word-y, we have a right boundary left of the null terminator
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        } else if (!is_Word_char(str[pos]) && is_Word_char(str[pos + 1])) {
            // if we have a delimiter followed by a Word char, we have a left boundary left of the Word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_LEFT, .pos = pos + 1};
        } else if (is_Word_char(str[pos]) && !is_Word_char(str[pos + 1])) {
            // if we have a Word char followed by a delimiter, we have a right boundary right of the Word char
            return (boundary_t){.type = E_BOUNDARY_TYPE_RIGHT, .pos = pos + 1};
        }
        return (boundary_t){.type = E_BOUNDARY_TYPE_NONE};
    }
}

int main() {
    string str;
    getline(cin, str);

    int len = str.length();
    for (int i = 0; i < len; i++) {
        boundary_t boundary = maybe_Word_boundary(str, i);
        if (boundary.type == E_BOUNDARY_TYPE_LEFT) {
            // whatever
        } else if (boundary.type == E_BOUNDARY_TYPE_RIGHT) {
            // whatever
        }
    }
}

Come puoi vedere, il codice è molto semplice da capire e mettere a punto, e l'uso effettivo del codice è molto breve e semplice. L'uso di C++ non dovrebbe impedirci di scrivere il codice più semplice e più facilmente personalizzabile, anche se questo significa non usare l'STL. Penserei che questo è un esempio di ciò che Linus Torvalds potrebbe chiamare "gusto" , poiché abbiamo eliminato tutta la logica di cui non abbiamo bisogno mentre scriviamo in uno stile che naturalmente consente di gestire più casi quando e se il è necessario gestirli.

Ciò che potrebbe migliorare questo codice potrebbe essere l'uso di enum class, accettando un puntatore a is_Word_char in maybe_Word_boundary invece di richiamare is_Word_char direttamente e passare un lambda.

0
okovko

Grazie @Jairo Abdiel Toribio Cisneros. Funziona per me ma la tua funzione restituisce qualche elemento vuoto. Quindi per il reso senza vuoto ho modificato con il seguente:

std::vector<std::string> split(std::string str, const char* delim) {
    std::vector<std::string> v;
    std::string tmp;

    for(std::string::const_iterator i = str.begin(); i <= str.end(); ++i) {
        if(*i != *delim && i != str.end()) {
            tmp += *i;
        } else {
            if (tmp.length() > 0) {
                v.Push_back(tmp);
            }
            tmp = "";
        }
    }

    return v;
}

Usando:

std::string s = "one:two::three";
std::string delim = ":";
std::vector<std::string> vv = split(s, delim.c_str());
0
Kakashi

la mia implementazione generale per string e u32string ~, usando la firma boost::algorithm::split.

template<typename CharT, typename UnaryPredicate>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           UnaryPredicate predicate)
{
    using ST = std::basic_string<CharT>;
    using std::swap;
    std::vector<ST> tmp_result;
    auto iter = s.cbegin(),
         end_iter = s.cend();
    while (true)
    {
        /**
         * Edge case: empty str -> Push an empty str and exit.
         */
        auto find_iter = find_if(iter, end_iter, predicate);
        tmp_result.emplace_back(iter, find_iter);
        if (find_iter == end_iter) { break; }
        iter = ++find_iter; 
    }
    swap(tmp_result, split_result);
}


template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const std::basic_string<CharT>& char_candidate)
{
    std::unordered_set<CharT> candidate_set(char_candidate.cbegin(),
                                            char_candidate.cend());
    auto predicate = [&candidate_set](const CharT& c) {
        return candidate_set.count(c) > 0U;
    };
    return split(split_result, s, predicate);
}

template<typename CharT>
void split(std::vector<std::basic_string<CharT>>& split_result,
           const std::basic_string<CharT>& s,
           const CharT* literals)
{
    return split(split_result, s, std::basic_string<CharT>(literals));
}
0
小文件
#include <iostream>
#include <string>
#include <deque>

std::deque<std::string> split(
    const std::string& line, 
    std::string::value_type delimiter,
    bool skipEmpty = false
) {
    std::deque<std::string> parts{};

    if (!skipEmpty && !line.empty() && delimiter == line.at(0)) {
        parts.Push_back({});
    }

    for (const std::string::value_type& c : line) {
        if (
            (
                c == delimiter 
                &&
                (skipEmpty ? (!parts.empty() && !parts.back().empty()) : true)
            )
            ||
            (c != delimiter && parts.empty())
        ) {
            parts.Push_back({});
        }

        if (c != delimiter) {
            parts.back().Push_back(c);
        }
    }

    if (skipEmpty && !parts.empty() && parts.back().empty()) {
        parts.pop_back();
    }

    return parts;
}

void test(const std::string& line) {
    std::cout << line << std::endl;

    std::cout << "skipEmpty=0 |";
    for (const std::string& part : split(line, ':')) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << "skipEmpty=1 |";
    for (const std::string& part : split(line, ':', true)) {
        std::cout << part << '|';
    }
    std::cout << std::endl;

    std::cout << std::endl;
}

int main() {
    test("foo:bar:::baz");
    test("");
    test("foo");
    test(":");
    test("::");
    test(":foo");
    test("::foo");
    test(":foo:");
    test(":foo::");

    return 0;
}

Produzione:

foo:bar:::baz
skipEmpty=0 |foo|bar|||baz|
skipEmpty=1 |foo|bar|baz|


skipEmpty=0 |
skipEmpty=1 |

foo
skipEmpty=0 |foo|
skipEmpty=1 |foo|

:
skipEmpty=0 |||
skipEmpty=1 |

::
skipEmpty=0 ||||
skipEmpty=1 |

:foo
skipEmpty=0 ||foo|
skipEmpty=1 |foo|

::foo
skipEmpty=0 |||foo|
skipEmpty=1 |foo|

:foo:
skipEmpty=0 ||foo||
skipEmpty=1 |foo|

:foo::
skipEmpty=0 ||foo|||
skipEmpty=1 |foo|
0
Oleg

se vuoi una stringa divisa con qualche carattere che puoi usare

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

using namespace std;
void replaceOtherChars(string &input, vector<char> &dividers)
{
    const char divider = dividers.at(0);
    int replaceIndex = 0;
    vector<char>::iterator it_begin = dividers.begin()+1,
        it_end= dividers.end();
    for(;it_begin!=it_end;++it_begin)
    {
        replaceIndex = 0;
        while(true)
        {
            replaceIndex=input.find_first_of(*it_begin,replaceIndex);
            if(replaceIndex==-1)
                break;
            input.at(replaceIndex)=divider;
        }
    }
}
vector<string> split(string str, vector<char> chars, bool missEmptySpace =true )
{
    vector<string> result;
    const char divider = chars.at(0);
    replaceOtherChars(str,chars);
    stringstream stream;
    stream<<str;    
    string temp;
    while(getline(stream,temp,divider))
    {
        if(missEmptySpace && temp.empty())
            continue;
        result.Push_back(temp);
    }
    return result;
}
int main()
{
    string str ="milk, pigs.... hot-dogs ";
    vector<char> arr;
    arr.Push_back(' '); arr.Push_back(','); arr.Push_back('.');
    vector<string> result = split(str,arr);
    vector<string>::iterator it_begin= result.begin(),
        it_end= result.end();
    for(;it_begin!=it_end;++it_begin)
    {
        cout<<*it_begin<<endl;
    }
return 0;
}
0
h.o.m.a.n

La mia implementazione può essere una soluzione alternativa:

std::vector<std::wstring> SplitString(const std::wstring & String, const std::wstring & Seperator)
{
    std::vector<std::wstring> Lines;
    size_t stSearchPos = 0;
    size_t stFoundPos;
    while (stSearchPos < String.size() - 1)
    {
        stFoundPos = String.find(Seperator, stSearchPos);
        stFoundPos = (stFoundPos == std::string::npos) ? String.size() : stFoundPos;
        Lines.Push_back(String.substr(stSearchPos, stFoundPos - stSearchPos));
        stSearchPos = stFoundPos + Seperator.size();
    }
    return Lines;
}

Codice di prova:

std::wstring MyString(L"Part 1SEPsecond partSEPlast partSEPend");
std::vector<std::wstring> Parts = IniFile::SplitString(MyString, L"SEP");
std::wcout << L"The string: " << MyString << std::endl;
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}
std::wcout << std::endl;
MyString = L"this,time,a,comma separated,string";
std::wcout << L"The string: " << MyString << std::endl;
Parts = IniFile::SplitString(MyString, L",");
for (std::vector<std::wstring>::const_iterator it=Parts.begin(); it<Parts.end(); ++it)
{
    std::wcout << *it << L"<---" << std::endl;
}

Uscita del codice di prova:

The string: Part 1SEPsecond partSEPlast partSEPend
Part 1<---
second part<---
last part<---
end<---

The string: this,time,a,comma separated,string
this<---
time<---
a<---
comma separated<---
string<---
0
hkBattousai

Questa è un'estensione di una delle risposte migliori. Ora supporta l'impostazione di un numero massimo di elementi restituiti, N. L'ultimo bit della stringa finirà nell'elemento Nth. Il parametro MAXELEMENTS è facoltativo, se impostato su 0 di default restituirà un unlimited quantità di elementi. :-)

.h:

class Myneatclass {
public:
    static std::vector<std::string>& split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS = 0);
    static std::vector<std::string> split(const std::string &s, char delim, const size_t MAXELEMENTS = 0);
};

cpp:

std::vector<std::string>& Myneatclass::split(const std::string &s, char delim, std::vector<std::string> &elems, const size_t MAXELEMENTS) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.Push_back(item);
        if (MAXELEMENTS > 0 && !ss.eof() && elems.size() + 1 >= MAXELEMENTS) {
            std::getline(ss, item);
            elems.Push_back(item);
            break;
        }
    }
    return elems;
}
std::vector<std::string> Myneatclass::split(const std::string &s, char delim, const size_t MAXELEMENTS) {
    std::vector<std::string> elems;
    split(s, delim, elems, MAXELEMENTS);
    return elems;
}
0
Jonny

sono molto in ritardo per la festa, ma so che stavo pensando al modo più elegante di farlo se ti fosse stata assegnata una gamma di delimitatori piuttosto che di spazi vuoti, e non avresti usato nient'altro che la libreria standard.

Ecco i miei pensieri:

Per dividere le parole in un vettore stringa mediante una sequenza di delimitatori:

template<class Container>
std::vector<std::string> split_by_delimiters(const std::string& input, const Container& delimiters)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if(current, end(input), not_in(delimiters));
        if (first == end(input)) break;
        auto last = find_if(first, end(input), is_in(delimiters));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

per dividere l'altro modo, fornendo una sequenza di caratteri validi:

template<class Container>
std::vector<std::string> split_by_valid_chars(const std::string& input, const Container& valid_chars)
{
    std::vector<std::string> result;

    for (auto current = begin(input) ; current != end(input) ; )
    {
        auto first = find_if(current, end(input), is_in(valid_chars));
        if (first == end(input)) break;
        auto last = find_if(first, end(input), not_in(valid_chars));
        result.emplace_back(first, last);
        current = last;
    }
    return result;
}

is_in e not_in sono così definiti:

namespace detail {
    template<class Container>
    struct is_in {
        is_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) != end(_charset);
        }

        const Container& _charset;
    };

    template<class Container>
    struct not_in {
        not_in(const Container& charset)
        : _charset(charset)
        {}

        bool operator()(char c) const
        {
            return find(begin(_charset), end(_charset), c) == end(_charset);
        }

        const Container& _charset;
    };

}

template<class Container>
detail::not_in<Container> not_in(const Container& c)
{
    return detail::not_in<Container>(c);
}

template<class Container>
detail::is_in<Container> is_in(const Container& c)
{
    return detail::is_in<Container>(c);
}
0
Richard Hodges