Frage

Java verfügt über eine praktische Split-Methode:

String str = "The quick brown fox";
String[] results = str.split(" ");

Gibt es eine einfache Möglichkeit, dies in C++ zu tun?

War es hilfreich?

Lösung

Ihr einfacher Fall kann leicht aufgebaut wird mit den std::string::find Verfahren . Jedoch einen Blick auf Boost.Tokenizer . Es ist toll. Boost-Regel hat einige sehr coole String-Tools.

Andere Tipps

Die Erhöhung tokenizer Klasse diese Art machen kann die Sache ganz einfach:

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

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

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

Aktualisiert für C ++ 11:

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

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

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

Hier ist ein echtes einfach:

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

vector<string> split(const char *str, char c = ' ')
{
    vector<string> result;

    do
    {
        const char *begin = str;

        while(*str != c && *str)
            str++;

        result.push_back(string(begin, str));
    } while (0 != *str++);

    return result;
}

Mit strtok. Meiner Meinung nach gibt es keine Notwendigkeit, eine Klasse um Tokenisieren zu bauen, es sei denn strtok Sie nicht mit dem, was Sie brauchen. Es ist vielleicht nicht, aber in mehr als 15 Jahren von verschiedenem Parsing-Code in C und C ++ zu schreiben, habe ich immer verwendet strtok. Hier ist ein Beispiel

char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
    printf ("Token: %s\n", p);
    p = strtok(NULL, " ");
}

Ein paar Einschränkungen (was Ihre Bedürfnisse vielleicht nicht passen). Der String ist „zerstört“ in den Prozess, was bedeutet, dass EOS-Zeichen in den inline delimter Flecken angeordnet sind. Korrekte Verwendung muss möglicherweise eine nicht-const-Version des Strings machen. Sie können auch die Liste der Mitte Parse-Trennzeichen ändern.

In meiner eigenen Meinung nach ist der obige Code wesentlich einfacher und einfacher zu bedienen als eine separate Klasse für sie zu schreiben. Für mich ist dies eine der Funktionen, die die Sprache bietet und es tut es gut und sauber. Es ist einfach eine „C basierte“ Lösung. Es ist angemessen, es ist einfach, und Sie haben nicht viel zusätzlichen Code schreiben: -)

Eine weitere schnelle Möglichkeit ist getline zu verwenden. So etwas wie:

stringstream ss("bla bla");
string s;

while (getline(ss, s, ' ')) {
 cout << s << endl;
}

Wenn Sie möchten, können Sie ein einfaches split() Verfahren machen eine vector<string> Rückkehr, das ist sehr nützlich.

Sie können Ströme verwenden, Iteratoren und den Kopieralgorithmus dies ziemlich direkt zu tun.

#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>

int main()
{
  std::string str = "The quick brown fox";

  // construct a stream from the string
  std::stringstream strstr(str);

  // use stream iterators to copy the stream to the vector as whitespace separated strings
  std::istream_iterator<std::string> it(strstr);
  std::istream_iterator<std::string> end;
  std::vector<std::string> results(it, end);

  // send the vector to stdout.
  std::ostream_iterator<std::string> oit(std::cout);
  std::copy(results.begin(), results.end(), oit);
}

Keine Straftat Leute, aber für so ein einfaches Problem, Sie machen Dinge Weg zu kompliziert. Es gibt eine Menge Gründe, Erhöhung . Aber für etwas so einfach, es ist wie eine Fliege mit einem 20 # Schlitten schlagen.

void
split( vector<string> & theStringVector,  /* Altered/returned value */
       const  string  & theString,
       const  string  & theDelimiter)
{
    UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.

    size_t  start = 0, end = 0;

    while ( end != string::npos)
    {
        end = theString.find( theDelimiter, start);

        // If at end, use length=maxLength.  Else use length=end-start.
        theStringVector.push_back( theString.substr( start,
                       (end == string::npos) ? string::npos : end - start));

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = (   ( end > (string::npos - theDelimiter.size()) )
                  ?  string::npos  :  end + theDelimiter.size());
    }
}

Zum Beispiel (für Dougs Fall)

#define SHOW(I,X)   cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl

int
main()
{
    vector<string> v;

    split( v, "A:PEP:909:Inventory Item", ":" );

    for (unsigned int i = 0;  i < v.size();   i++)
        SHOW( i, v[i] );
}

Und ja, könnten wir split () haben einen neuen Vektor zurückzukehren, anstatt eine nebenbei. Es ist trivial zu wickeln und Überlastung. Aber je nachdem, was ich tue, finde ich es oft besser wiederverwenden bereits bestehende Objekte, statt immer neue zu schaffen. (Nur so lange, wie ich vergessen Sie nicht, den Vektor zu leeren dazwischen!)

Referenz: http://www.cplusplus.com/reference/string/string / .

(Ich schrieb ursprünglich eine Antwort auf Dougs Frage: C ++ Strings Ändern und Extrahieren basierend auf Separatoren (geschlossen) . Aber da Martin Yorker diese Frage mit einem Zeiger hier geschlossen ... ich werde einfach meinen Code verallgemeinern.)

Erhöhung hat eine starke Split-Funktion: boost :: Algorithmus :: Split .

Beispielprogramm:

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

int main() {
    auto s = "a,b, c ,,e,f,";
    std::vector<std::string> fields;
    boost::split(fields, s, boost::is_any_of(","));
    for (const auto& field : fields)
        std::cout << "\"" << field << "\"\n";
    return 0;
}

Ausgabe:

"a"
"b"
" c "
""
"e"
"f"
""

Eine Lösung mit regex_token_iterators:

#include <iostream>
#include <regex>
#include <string>

using namespace std;

int main()
{
    string str("The quick brown fox");

    regex reg("\\s+");

    sregex_token_iterator iter(str.begin(), str.end(), reg, -1);
    sregex_token_iterator end;

    vector<string> vec(iter, end);

    for (auto a : vec)
    {
        cout << a << endl;
    }
}

Ich weiß, dass Sie für eine C ++ Lösung gefragt, aber Sie könnten betrachten dies hilfreich:

Qt

#include <QString>

...

QString str = "The quick brown fox"; 
QStringList results = str.split(" "); 

Der Vorteil gegenüber Erhöhung in diesem Beispiel ist, dass es zu einer Zuordnung zu Ihrem Beitrag Code ein direkter ist.

Mehr unter Qt Dokumentation

Hier ist ein Beispiel tokenizer Klasse, die möglicherweise tun, was Sie wollen

//Header file
class Tokenizer 
{
    public:
        static const std::string DELIMITERS;
        Tokenizer(const std::string& str);
        Tokenizer(const std::string& str, const std::string& delimiters);
        bool NextToken();
        bool NextToken(const std::string& delimiters);
        const std::string GetToken() const;
        void Reset();
    protected:
        size_t m_offset;
        const std::string m_string;
        std::string m_token;
        std::string m_delimiters;
};

//CPP file
const std::string Tokenizer::DELIMITERS(" \t\n\r");

Tokenizer::Tokenizer(const std::string& s) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(DELIMITERS) {}

Tokenizer::Tokenizer(const std::string& s, const std::string& delimiters) :
    m_string(s), 
    m_offset(0), 
    m_delimiters(delimiters) {}

bool Tokenizer::NextToken() 
{
    return NextToken(m_delimiters);
}

bool Tokenizer::NextToken(const std::string& delimiters) 
{
    size_t i = m_string.find_first_not_of(delimiters, m_offset);
    if (std::string::npos == i) 
    {
        m_offset = m_string.length();
        return false;
    }

    size_t j = m_string.find_first_of(delimiters, i);
    if (std::string::npos == j) 
    {
        m_token = m_string.substr(i);
        m_offset = m_string.length();
        return true;
    }

    m_token = m_string.substr(i, j - i);
    m_offset = j;
    return true;
}

Beispiel:

std::vector <std::string> v;
Tokenizer s("split this string", " ");
while (s.NextToken())
{
    v.push_back(s.GetToken());
}

Dies ist eine einfache STL-only-Lösung (~ 5 Zeilen!) Mit std::find und std::find_first_not_of die Wiederholungen des Trennzeichens (wie Leerzeichen oder Punkte zum Beispiel) behandelt sowie vorderen und hinteren Begrenzungszeichen:

#include <string>
#include <vector>

void tokenize(std::string str, std::vector<string> &token_v){
    size_t start = str.find_first_not_of(DELIMITER), end=start;

    while (start != std::string::npos){
        // Find next occurence of delimiter
        end = str.find(DELIMITER, start);
        // Push back the token found into vector
        token_v.push_back(str.substr(start, end-start));
        // Skip all occurences of the delimiter to find new start
        start = str.find_first_not_of(DELIMITER, end);
    }
}

Ausprobieren leben !

pystring ist eine kleine Bibliothek, die eine Reihe von Python-String-Funktionen implementiert, einschließlich der Split-Methode:

#include <string>
#include <vector>
#include "pystring.h"

std::vector<std::string> chunks;
pystring::split("this string", chunks);

// also can specify a separator
pystring::split("this-string", chunks, "-");

Ich gab diese Antwort für ähnliche Frage.
Das Rad nicht neu erfinden. Ich habe eine Reihe von Bibliotheken verwendet und die schnellste und flexibelste Ich bin gekommen, across ist: C ++ String Toolkit Bibliothek .

Hier ist ein Beispiel dafür, wie es zu benutzen, dass ich sonst wo auf der Stackoverflow gebucht haben.

#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 t("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 u("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;
}

Dieses Beispiel prüfen. Es könnte Ihnen helfen ..

#include <iostream>
#include <sstream>

using namespace std;

int main ()
{
    string tmps;
    istringstream is ("the dellimiter is the space");
    while (is.good ()) {
        is >> tmps;
        cout << tmps << "\n";
    }
    return 0;
}

MFC / ATL hat einen sehr schönen tokenizer. Von MSDN:

CAtlString str( "%First Second#Third" );
CAtlString resToken;
int curPos= 0;

resToken= str.Tokenize("% #",curPos);
while (resToken != "")
{
   printf("Resulting token: %s\n", resToken);
   resToken= str.Tokenize("% #",curPos);
};

Output

Resulting Token: First
Resulting Token: Second
Resulting Token: Third

Sie können einfach eine Bibliothek für reguläre Ausdrücke und lösen, dass reguläre Ausdrücke verwenden.

Verwenden Ausdruck (\ w +) und die Variable in \ 1 (oder $ 1 in Abhängigkeit von der Bibliothek Implementierung von regulären Ausdrücken).

Wenn Sie bereit sind, C zu verwenden, können Sie die strtok Funktion. Sie sollten ihr Augenmerk auf Multi-Thread-Probleme zahlen, wenn es zu benutzen.

Für einfache Sachen, die ich benutzen Sie einfach die folgenden:

unsigned TokenizeString(const std::string& i_source,
                        const std::string& i_seperators,
                        bool i_discard_empty_tokens,
                        std::vector<std::string>& o_tokens)
{
    unsigned prev_pos = 0;
    unsigned pos = 0;
    unsigned number_of_tokens = 0;
    o_tokens.clear();
    pos = i_source.find_first_of(i_seperators, pos);
    while (pos != std::string::npos)
    {
        std::string token = i_source.substr(prev_pos, pos - prev_pos);
        if (!i_discard_empty_tokens || token != "")
        {
            o_tokens.push_back(i_source.substr(prev_pos, pos - prev_pos));
            number_of_tokens++;
        }

        pos++;
        prev_pos = pos;
        pos = i_source.find_first_of(i_seperators, pos);
    }

    if (prev_pos < i_source.length())
    {
        o_tokens.push_back(i_source.substr(prev_pos));
        number_of_tokens++;
    }

    return number_of_tokens;
}

Cowardly Haftungsausschluss: Ich schreibe Echtzeit-Datenverarbeitungssoftware, wo die Daten in durch Binärdateien kommen, Sockets oder einen API-Aufruf (I / O-Karten, Kamera). Ich benutze nie diese Funktion für etwas komplizierter oder zeitkritisch als das Lesen externe Konfigurationsdateien beim Start.

Viele zu kompliziert Vorschläge hier. Versuchen Sie diese einfache std :: string Lösung:

using namespace std;

string someText = ...

string::size_type tokenOff = 0, sepOff = tokenOff;
while (sepOff != string::npos)
{
    sepOff = someText.find(' ', sepOff);
    string::size_type tokenLen = (sepOff == string::npos) ? sepOff : sepOff++ - tokenOff;
    string token = someText.substr(tokenOff, tokenLen);
    if (!token.empty())
        /* do something with token */;
    tokenOff = sepOff;
}

Ich dachte, das war es, was der >> Operator auf String-Streams war:

string word; sin >> word;

Adam Pierces Antwort bietet eine handgesponnene tokenizer in einem const char* nehmen. Es ist ein bisschen problematischer mit Iteratoren zu tun, weil ein string Ende Iterator Inkrementieren ist nicht definiert. Das heißt, da string str{ "The quick brown fox" } wir können dies mit Sicherheit erreichen:

auto start = find(cbegin(str), cend(str), ' ');
vector<string> tokens{ string(cbegin(str), start) };

while (start != cend(str)) {
    const auto finish = find(++start, cend(str), ' ');

    tokens.push_back(string(start, finish));
    start = finish;
}

Live-Beispiel


Wenn Sie sich auf abstrakte Komplexität der Suche unter Verwendung von Standard-Funktionalität, wie On Freund schlägt strtok ist eine einfache Option:

vector<string> tokens;

for (auto i = strtok(data(str), " "); i != nullptr; i = strtok(nullptr, " ")) tokens.push_back(i);

Wenn Sie keinen Zugang zu C 17 ++ Sie benötigen data(str) wie in diesem Beispiel ersetzen: http : //ideone.com/8kAGoa

Obwohl im Beispiel nicht gezeigt, strtok braucht nicht das gleiche Trennzeichen für jedes Token zu verwenden. Zusammen mit dieser Vorteil allerdings gibt es mehrere Nachteile:

  1. strtok kann nicht auf mehreren strings gleichzeitig verwendet werden: Entweder ein nullptr muss Zeichenüber die aktuelle string oder eine neue char* weitergeben werden müssen tokenize weitergegeben werden (es gibt einige Nicht-Standard-Implementierungen, die dies jedoch unterstützen, wie zum Beispiel: strtok_s )
  2. Aus dem gleichen Grunde strtok kann nicht auf mehreren Threads gleichzeitig verwendet werden (dies jedoch Implementierung definiert sein kann, zum Beispiel: Visual Studio-Implementierung ist die thread~~POS=TRUNC )
  3. Beim strtok die string ändert es auf in Betrieb ist, so kann es nicht auf const strings, const char*s verwendet werden, oder Zeichenketten, mit strtok einer dieser Punkte auf tokenize oder auf einem string zu betreiben, die Inhalte ist gewahrt werden müssen, str hätte kopiert werden, dann könnte die Kopie auf betrieben werden

Sowohl die bisherigen Methoden können keine Zeichen übersetzten vector an Ort und Stelle erzeugen, das heißt, ohne sie in eine Helferfunktion abstrahiert sie nicht const vector<string> tokens initialisieren. Diese Funktionalität und die Fähigkeit zu akzeptieren jeder white-space Trennzeichen kann ein istream_iterator . Zum Beispiel gegeben: const string str{ "The quick \tbrown \nfox" } wir können dies tun:

istringstream is{ str };
const vector<string> tokens{ istream_iterator<string>(is), istream_iterator<string>() };

Live-Beispiel

Der erforderliche Aufbau eines istringstream für diese Option hat weit höhere Kosten als die bisherigen zwei Optionen, aber diese Kosten üblicherweise in Kosten der string Zuordnung verborgen ist.


Falls keine der oben genannten Optionen flexable genug für Ihre tokenization Bedürfnisse sind, die flexibelste Option wird mit einem regex_token_iterator natürlich mit dieser Flexibilität kommt höhere Kosten, aber auch dies ist in der string Verrechnungskostenart wahrscheinlich versteckt. Sagen Sie zum Beispiel, das wir auf der Grundlage nicht entgangen Kommas tokenize wollen, auch white-space essen, angesichts der folgenden Eingabe: const string str{ "The ,qu\\,ick ,\tbrown, fox" } wir dies tun können:

const regex re{ "\\s*((?:[^\\\\,]|\\\\.)*?)\\s*(?:,|$)" };
const vector<string> tokens{ sregex_token_iterator(cbegin(str), cend(str), re, 1), sregex_token_iterator() };

Live-Beispiel

Hier ist ein Ansatz, den Sie (wie strsep) Kontrolle darüber, ob leere Token enthalten kann oder (wie strtok) ausgeschlossen.

#include <string.h> // for strchr and strlen

/*
 * want_empty_tokens==true  : include empty tokens, like strsep()
 * want_empty_tokens==false : exclude empty tokens, like strtok()
 */
std::vector<std::string> tokenize(const char* src,
                                  char delim,
                                  bool want_empty_tokens)
{
  std::vector<std::string> tokens;

  if (src and *src != '\0') // defensive
    while( true )  {
      const char* d = strchr(src, delim);
      size_t len = (d)? d-src : strlen(src);

      if (len or want_empty_tokens)
        tokens.push_back( std::string(src, len) ); // capture token

      if (d) src += len+1; else break;
    }

  return tokens;
}

Es scheint mir seltsam, dass hier mit allen uns Geschwindigkeit bewusst Nerds auf, damit niemand eine Version vorgelegt hat, die eine Kompilierung verwendet generierte Tabelle für das Trennzeichen (zB Implementierung weiter unten) nachschlagen. Mit Hilfe einer Nachschlagetabelle und Iteratoren sollte std :: regex in Effizienz schlagen, wenn Sie benötigen es nicht regex zu schlagen, nur verwenden, dessen Standard wie von C ++ 11 und super flexibel.

Einige regex vorgeschlagen hat bereits, aber für den noobs hier ist ein verpacktes Beispiel, die genau das tun soll, was der OP erwartet:

std::vector<std::string> split(std::string::const_iterator it, std::string::const_iterator end, std::regex e = std::regex{"\\w+"}){
    std::smatch m{};
    std::vector<std::string> ret{};
    while (std::regex_search (it,end,m,e)) {
        ret.emplace_back(m.str());              
        std::advance(it, m.position() + m.length()); //next start position = match position + match length
    }
    return ret;
}
std::vector<std::string> split(const std::string &s, std::regex e = std::regex{"\\w+"}){  //comfort version calls flexible version
    return split(s.cbegin(), s.cend(), std::move(e));
}
int main ()
{
    std::string str {"Some people, excluding those present, have been compile time constants - since puberty."};
    auto v = split(str);
    for(const auto&s:v){
        std::cout << s << std::endl;
    }
    std::cout << "crazy version:" << std::endl;
    v = split(str, std::regex{"[^e]+"});  //using e as delim shows flexibility
    for(const auto&s:v){
        std::cout << s << std::endl;
    }
    return 0;
}

Wenn wir müssen schneller sein und die Einschränkung zu akzeptieren, dass alle Zeichen 8 Bits sein müssen, können wir eine Verweistabelle zum Zeitpunkt der Kompilierung mit metaprogramming machen:

template<bool...> struct BoolSequence{};        //just here to hold bools
template<char...> struct CharSequence{};        //just here to hold chars
template<typename T, char C> struct Contains;   //generic
template<char First, char... Cs, char Match>    //not first specialization
struct Contains<CharSequence<First, Cs...>,Match> :
    Contains<CharSequence<Cs...>, Match>{};     //strip first and increase index
template<char First, char... Cs>                //is first specialization
struct Contains<CharSequence<First, Cs...>,First>: std::true_type {}; 
template<char Match>                            //not found specialization
struct Contains<CharSequence<>,Match>: std::false_type{};

template<int I, typename T, typename U> 
struct MakeSequence;                            //generic
template<int I, bool... Bs, typename U> 
struct MakeSequence<I,BoolSequence<Bs...>, U>:  //not last
    MakeSequence<I-1, BoolSequence<Contains<U,I-1>::value,Bs...>, U>{};
template<bool... Bs, typename U> 
struct MakeSequence<0,BoolSequence<Bs...>,U>{   //last  
    using Type = BoolSequence<Bs...>;
};
template<typename T> struct BoolASCIITable;
template<bool... Bs> struct BoolASCIITable<BoolSequence<Bs...>>{
    /* could be made constexpr but not yet supported by MSVC */
    static bool isDelim(const char c){
        static const bool table[256] = {Bs...};
        return table[static_cast<int>(c)];
    }   
};
using Delims = CharSequence<'.',',',' ',':','\n'>;  //list your custom delimiters here
using Table = BoolASCIITable<typename MakeSequence<256,BoolSequence<>,Delims>::Type>;

Mit der im Ort eine getNextToken Funktion zu machen ist einfach:

template<typename T_It>
std::pair<T_It,T_It> getNextToken(T_It begin,T_It end){
    begin = std::find_if(begin,end,std::not1(Table{})); //find first non delim or end
    auto second = std::find_if(begin,end,Table{});      //find first delim or end
    return std::make_pair(begin,second);
}

Mit ihm ist auch einfach:

int main() {
    std::string s{"Some people, excluding those present, have been compile time constants - since puberty."};
    auto it = std::begin(s);
    auto end = std::end(s);
    while(it != std::end(s)){
        auto token = getNextToken(it,end);
        std::cout << std::string(token.first,token.second) << std::endl;
        it = token.second;
    }
    return 0;
}

Hier ist ein anschauliches Beispiel: http://ideone.com/GKtkLQ

Ich weiß, diese Frage ist schon beantwortet, aber ich möchte dazu beitragen. Vielleicht ist meine Lösung ein einfach wenig, aber das ist, was ich kam mit:

vector<string> get_words(string const& text)
{
    vector<string> result;
    string tmp = text;

    size_t first_pos = 0;
    size_t second_pos = tmp.find(" ");;

    while (second_pos != string::npos)
    {
        if (first_pos != second_pos)
        {
            string word = tmp.substr(first_pos, second_pos - first_pos);
            result.push_back(word);
        }
        tmp = tmp.substr(second_pos + 1);
        second_pos = tmp.find(" ");
    }

    result.push_back(tmp);

    return result;
}

Bitte kommentieren, wenn es ein besserer Ansatz, um etwas in meinem Code ist oder wenn etwas falsch ist.

Es gibt keinen direkten Weg, dies zu tun. Siehe dieses Code-Projekt Quellcode um herauszufinden, wie eine Klasse für diese bauen .

Sie können die Vorteile der boost :: nehmen make_find_iterator. Etwas ähnliches wie folgt aus:

template<typename CH>
inline vector< basic_string<CH> > tokenize(
    const basic_string<CH> &Input,
    const basic_string<CH> &Delimiter,
    bool remove_empty_token
    ) {

    typedef typename basic_string<CH>::const_iterator string_iterator_t;
    typedef boost::find_iterator< string_iterator_t > string_find_iterator_t;

    vector< basic_string<CH> > Result;
    string_iterator_t it = Input.begin();
    string_iterator_t it_end = Input.end();
    for(string_find_iterator_t i = boost::make_find_iterator(Input, boost::first_finder(Delimiter, boost::is_equal()));
        i != string_find_iterator_t();
        ++i) {
        if(remove_empty_token){
            if(it != i->begin())
                Result.push_back(basic_string<CH>(it,i->begin()));
        }
        else
            Result.push_back(basic_string<CH>(it,i->begin()));
        it = i->end();
    }
    if(it != it_end)
        Result.push_back(basic_string<CH>(it,it_end));

    return Result;
}

Wenn die maximale Länge der Eingabezeichenfolge ist bekannt seinen Token versehen, kann man dies ausnutzen und eine sehr schnelle Version implementieren. Ich bin die Grundidee unten skizzieren, die sowohl von strtok inspiriert wurde () und die „Suffixarray“ -Daten Struktur beschrieben Jon Bentley „Programming Perls“ 2. Auflage, Kapitel 15. Die C ++ Klasse in diesem Fall gibt nur einige Organisation und Komfort von Nutzen. Die Implementierung gezeigt kann leicht zur Entfernung verlängert werden führende und nachgestellte Leerzeichen in den Token.

Grundsätzlich kann man die Trennzeichen mit strang abschließende ‚\ 0'-Zeichen ersetzen und stellen Zeiger auf die Token withing der modifizierten Zeichenfolge. Im Extremfall, wenn die Zeichenfolge nur von Separatoren bestehen, erhält man fade Länge plus 1 resultierende leere Token. Es ist praktisch, die Zeichenfolge zu duplizieren zu geändert werden.

Header-Datei:

class TextLineSplitter
{
public:

    TextLineSplitter( const size_t max_line_len );

    ~TextLineSplitter();

    void            SplitLine( const char *line,
                               const char sep_char = ',',
                             );

    inline size_t   NumTokens( void ) const
    {
        return mNumTokens;
    }

    const char *    GetToken( const size_t token_idx ) const
    {
        assert( token_idx < mNumTokens );
        return mTokens[ token_idx ];
    }

private:
    const size_t    mStorageSize;

    char           *mBuff;
    char          **mTokens;
    size_t          mNumTokens;

    inline void     ResetContent( void )
    {
        memset( mBuff, 0, mStorageSize );
        // mark all items as empty:
        memset( mTokens, 0, mStorageSize * sizeof( char* ) );
        // reset counter for found items:
        mNumTokens = 0L;
    }
};

Implementattion-Datei:

TextLineSplitter::TextLineSplitter( const size_t max_line_len ):
    mStorageSize ( max_line_len + 1L )
{
    // allocate memory
    mBuff   = new char  [ mStorageSize ];
    mTokens = new char* [ mStorageSize ];

    ResetContent();
}

TextLineSplitter::~TextLineSplitter()
{
    delete [] mBuff;
    delete [] mTokens;
}


void TextLineSplitter::SplitLine( const char *line,
                                  const char sep_char   /* = ',' */,
                                )
{
    assert( sep_char != '\0' );

    ResetContent();
    strncpy( mBuff, line, mMaxLineLen );

    size_t idx       = 0L; // running index for characters

    do
    {
        assert( idx < mStorageSize );

        const char chr = line[ idx ]; // retrieve current character

        if( mTokens[ mNumTokens ] == NULL )
        {
            mTokens[ mNumTokens ] = &mBuff[ idx ];
        } // if

        if( chr == sep_char || chr == '\0' )
        { // item or line finished
            // overwrite separator with a 0-terminating character:
            mBuff[ idx ] = '\0';
            // count-up items:
            mNumTokens ++;
        } // if

    } while( line[ idx++ ] );
}

Ein Szenario der Nutzung sei:

// create an instance capable of splitting strings up to 1000 chars long:
TextLineSplitter spl( 1000 );
spl.SplitLine( "Item1,,Item2,Item3" );
for( size_t i = 0; i < spl.NumTokens(); i++ )
{
    printf( "%s\n", spl.GetToken( i ) );
}

Ausgabe:

Item1

Item2
Item3

boost::tokenizer ist dein Freund, aber in Erwägung ziehen Ihr Code portabel mit Bezug auf die Internationalisierung (i18n) Fragen von wstring / wchar_t statt der Legacy-string / char Typen verwendet wird.

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

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<wchar_t>,
                  wstring::const_iterator, wstring> Tok;

int main()
{
  wstring s;
  while (getline(wcin, s)) {
    char_separator<wchar_t> sep(L" "); // list of separator characters
    Tok tok(s, sep);
    for (Tok::iterator beg = tok.begin(); beg != tok.end(); ++beg) {
      wcout << *beg << L"\t"; // output (or store in vector)
    }
    wcout << L"\n";
  }
  return 0;
}

Einfach C ++ Code (Standard C ++ 98), mehrere Trennzeichen (angegeben in einem std :: string) akzeptiert, verwendet nur Vektoren, Streicher und Iteratoren.

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

std::vector<std::string> 
split(const std::string& str, const std::string& delim){
    std::vector<std::string> result;
    if (str.empty())
        throw std::runtime_error("Can not tokenize an empty string!");
    std::string::const_iterator begin, str_it;
    begin = str_it = str.begin(); 
    do {
        while (delim.find(*str_it) == std::string::npos && str_it != str.end())
            str_it++; // find the position of the first delimiter in str
        std::string token = std::string(begin, str_it); // grab the token
        if (!token.empty()) // empty token only when str starts with a delimiter
            result.push_back(token); // push the token into a vector<string>
        while (delim.find(*str_it) != std::string::npos && str_it != str.end())
            str_it++; // ignore the additional consecutive delimiters
        begin = str_it; // process the remaining tokens
        } while (str_it != str.end());
    return result;
}

int main() {
    std::string test_string = ".this is.a.../.simple;;test;;;END";
    std::string delim = "; ./"; // string containing the delimiters
    std::vector<std::string> tokens = split(test_string, delim);           
    for (std::vector<std::string>::const_iterator it = tokens.begin(); 
        it != tokens.end(); it++)
            std::cout << *it << std::endl;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top