2012-04-07 15 views
20

So che questo è un problema abbastanza facile, ma voglio solo risolvere per me stesso una volta per tuttesuddivisione di un testo da un carattere

vorrei semplicemente per dividere una stringa in un array utilizzando un carattere come il delimitatore di divisione. (Molto simile famosa .Split function() del C# 's. Posso naturalmente applicare l'approccio a forza bruta, ma mi chiedo se c'è qualcosa di meglio.

Finora l'ho cercato e probabilmente il più vicino approccio soluzione è l'utilizzo di strtok(), tuttavia grazie alla sua scomodità (la conversione stringa in un array di caratteri, ecc) non mi piace usarlo. c'è un modo più semplice per implementare questo?

Nota: Volevo enfatizzare questo perché la gente potrebbe chiedere "Come mai la forza bruta non funziona". La mia soluzione di forza bruta consisteva nel creare un ciclo, e usare il substr() all'interno. Tuttavia dal momento che richiede il punto di partenza e la lunghezza, non riesce quando voglio dividere una data. Perché l'utente potrebbe inserirla come 7/12/2012 o 07/3/2011, dove posso veramente dire la lunghezza prima di calcolare la prossima posizione del delimitatore '/'.

+0

possibile duplicato di [Splitting String C++] (http://stackoverflow.com/questions/275404/splitting-string-c) –

risposta

49

Utilizzo di vettori, stringhe e stringhe. Un po 'ingombrante ma fa il trucco.

std::stringstream test("this_is_a_test_string"); 
std::string segment; 
std::vector<std::string> seglist; 

while(std::getline(test, segment, '_')) 
{ 
    seglist.push_back(segment); 
} 
+0

In realtà questo tipo di approccio esattamente quello che sto cercando. Abbastanza facile da capire, nessun utilizzo di librerie esterne, solo molto semplice. Grazie a @thelazydeveloper! – Ali

+0

Ancora la soluzione migliore là fuori! –

2

Date un'occhiata a boost::tokenizer

Se vuoi per rotolare il proprio metodo, è possibile utilizzare std::string::find() per determinare i punti di divisione.

+2

Grazie per il suggerimento di ricerca stringa. Ami sempre le soluzioni ** std **! – Ali

9

Boost ha il split() si sta cercando in algorithm/string.hpp:

std::string sample = "07/3/2011"; 
std::vector<string> strs; 
boost::split(strs, sample, boost::is_any_of("/")); 
10

Un altro modo (C++ 11/boost) per le persone che amano RegEx. Personalmente sono un grande fan di RegEx per questo tipo di dati. IMO è molto più potente della semplice suddivisione delle stringhe utilizzando un delimitatore poiché puoi scegliere di essere molto più intelligente su ciò che costituisce i dati "validi", se lo desideri.

#include <string> 
#include <algorithm> // copy 
#include <iterator>  // back_inserter 
#include <regex>  // regex, sregex_token_iterator 
#include <vector> 

int main() 
{ 
    std::string str = "08/04/2012"; 
    std::vector<std::string> tokens; 
    std::regex re("\\d+"); 

    //start/end points of tokens in str 
    std::sregex_token_iterator 
     begin(str.begin(), str.end(), re), 
     end; 

    std::copy(begin, end, std::back_inserter(tokens)); 
} 
4

Un'altra possibilità è quella di infondere un ruscello con un'impostazione internazionale che utilizza uno speciale ctype sfaccettatura. Un flusso usa la faccetta del ctype per determinare cosa è "spazio bianco", che considera come separatori. Con una faccetta di tipo ctype che classifica il tuo carattere separatore come spazio bianco, la lettura può essere piuttosto banale.Ecco un modo per implementare la sfaccettatura:

struct field_reader: std::ctype<char> { 

    field_reader(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(table_size, std::ctype_base::mask()); 

     // we'll assume dates are either a/b/c or a-b-c: 
     rc['/'] = std::ctype_base::space; 
     rc['-'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

Usiamo che utilizzando imbue per raccontare un flusso di utilizzare un locale che lo include, poi leggere i dati da tale flusso:

std::istringstream in("07/3/2011"); 
in.imbue(std::locale(std::locale(), new field_reader); 

Con questo al suo posto, la scissione diventa quasi banale - basta inizializzare un vettore utilizzando un paio di istream_iterator s per leggere i pezzi dalla stringa (che è incorporato nel istringstream):

std::vector<std::string>((std::istream_iterator<std::string>(in), 
          std::istream_iterator<std::string>()); 

Ovviamente questo tende all'overkill se lo si usa solo in un posto. Se la usi molto, tuttavia, può fare molto per mantenere pulito il resto del codice.

0

Non mi piace per niente stringstream, anche se non sono sicuro del perché. Oggi ho scritto questa funzione per consentire la suddivisione di uno std::string con qualsiasi carattere o stringa arbitraria in un vettore. So che questa domanda è vecchia, ma volevo condividere un modo alternativo di dividere lo std::string.

Questo codice omette la parte della stringa che hai diviso dai risultati del tutto, anche se potrebbe essere facilmente modificata per includerli.

#include <string> 
#include <vector> 

void split(std::string str, std::string splitBy, std::vector<std::string>& tokens) 
{ 
    /* Store the original string in the array, so we can loop the rest 
    * of the algorithm. */ 
    tokens.push_back(str); 

    // Store the split index in a 'size_t' (unsigned integer) type. 
    size_t splitAt; 
    // Store the size of what we're splicing out. 
    size_t splitLen = splitBy.size(); 
    // Create a string for temporarily storing the fragment we're processing. 
    std::string frag; 
    // Loop infinitely - break is internal. 
    while(true) 
    { 
     /* Store the last string in the vector, which is the only logical 
     * candidate for processing. */ 
     frag = tokens.back(); 
     /* The index where the split is. */ 
     splitAt = frag.find(splitBy); 
     // If we didn't find a new split point... 
     if(splitAt == string::npos) 
     { 
      // Break the loop and (implicitly) return. 
      break; 
     } 
     /* Put everything from the left side of the split where the string 
     * being processed used to be. */ 
     tokens.back() = frag.substr(0, splitAt); 
     /* Push everything from the right side of the split to the next empty 
     * index in the vector. */ 
     tokens.push_back(frag.substr(splitAt+splitLen, frag.size()-(splitAt+splitLen))); 
    } 
} 

da usare, basta chiamare in questo modo ...

std::string foo = "This is some string I want to split by spaces."; 
std::vector<std::string> results; 
split(foo, " ", results); 

È ora possibile accedere a tutti i risultati nel vettore a volontà. Semplice come quello - no stringstream, niente librerie di terze parti, non tornare a C!

Problemi correlati