web-dev-qa-db-ger.com

Parse (Split) einer Zeichenfolge in C++ mit Zeichenfolge-Trennzeichen (Standard C++)

Ich analysiere eine Zeichenfolge in C++ mit folgendem Befehl:

string parsed,input="text to be parsed";
stringstream input_stringstream(input);

if(getline(input_stringstream,parsed,' '))
{
     // do some processing.
}

Das Parsen mit einem einzelnen Zeichen als Trennzeichen ist in Ordnung. Was aber, wenn ich einen String als Trennzeichen verwenden möchte.

Beispiel: Ich möchte teilen:

scott>=tiger

mit> = als Trennzeichen, damit ich Scott und Tiger bekommen kann. 

228

Sie können die Funktion std::string::find() verwenden, um die Position Ihres Zeichenfolge-Trennzeichens zu ermitteln, und dann mit std::string::substr() ein Token abrufen.

Beispiel:

std::string s = "scott>=tiger";
std::string delimiter = ">=";
std::string token = s.substr(0, s.find(delimiter)); // token is "scott"
  • Die Funktion find(const string& str, size_t pos = 0) gibt die Position des ersten Vorkommens von str in der Zeichenfolge zurück bzw. npos , falls die Zeichenfolge nicht gefunden wird.

  • Die Funktion substr(size_t pos = 0, size_t n = npos) gibt einen Teilstring des Objekts zurück, beginnend an Position pos und der Länge npos.


Wenn Sie mehrere Trennzeichen haben, nachdem Sie ein Token extrahiert haben, können Sie es entfernen (Trennzeichen enthalten), um mit nachfolgenden Extraktionen fortzufahren (wenn Sie den ursprünglichen String beibehalten möchten, verwenden Sie einfach s = s.substr(pos + delimiter.length());):

s.erase(0, s.find(delimiter) + delimiter.length());

Auf diese Weise können Sie leicht einen Loop erstellen, um jedes Token zu erhalten.

Vollständiges Beispiel 

std::string s = "scott>=tiger>=mushroom";
std::string delimiter = ">=";

size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
    token = s.substr(0, pos);
    std::cout << token << std::endl;
    s.erase(0, pos + delimiter.length());
}
std::cout << s << std::endl;

Ausgabe:

scott
tiger
mushroom
412
Vincenzo Pii

Diese Methode verwendet std::string::find, ohne die ursprüngliche Zeichenfolge zu mutieren, indem der Anfang und das Ende des vorherigen Teilzeichenfolgetokens gespeichert werden.

#include <iostream>
#include <string>

int main()
{
    std::string s = "scott>=tiger";
    std::string delim = ">=";

    auto start = 0U;
    auto end = s.find(delim);
    while (end != std::string::npos)
    {
        std::cout << s.substr(start, end - start) << std::endl;
        start = end + delim.length();
        end = s.find(delim, start);
    }

    std::cout << s.substr(start, end);
}
42
moswald

Mit der nächsten Funktion können Sie die Zeichenfolge teilen:

vector<string> split(const string& str, const string& delim)
{
    vector<string> tokens;
    size_t prev = 0, pos = 0;
    do
    {
        pos = str.find(delim, prev);
        if (pos == string::npos) pos = str.length();
        string token = str.substr(prev, pos-prev);
        if (!token.empty()) tokens.Push_back(token);
        prev = pos + delim.length();
    }
    while (pos < str.length() && prev < str.length());
    return tokens;
}
26
Sviatoslav

Mit strtok können Sie mehrere Zeichen als Trennzeichen übergeben. Ich wette, wenn Sie "> =" übergeben, wird Ihr Beispielstring korrekt aufgeteilt (obwohl> und = als einzelne Trennzeichen gezählt werden).

EDIT Wenn Sie c_str() nicht zur Konvertierung von Zeichenfolge in Zeichen * verwenden möchten, können Sie substr und find_first_of zum tokenize verwenden.

string token, mystring("scott>=tiger");
while(token != mystring){
  token = mystring.substr(0,mystring.find_first_of(">="));
  mystring = mystring.substr(mystring.find_first_of(">=") + 1);
  printf("%s ",token.c_str());
}
13
ryanbwork

Für Zeichenfolge-Begrenzer

Zeichenfolge basierend auf einem Zeichenfolge-Trennzeichen aufteilen. B. die Zeichenfolge "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih" basierend auf dem Zeichenfolgenbegrenzer "-+", wird die Ausgabe {"adsf", "qwret", "nvfkbdsj", "orthdfjgh", "dfjrleih"}.__ ausgegeben.

#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

// for string delimiter
vector<string> split (string s, string delimiter) {
    size_t pos_start = 0, pos_end, delim_len = delimiter.length();
    string token;
    vector<string> res;

    while ((pos_end = s.find (delimiter, pos_start)) != string::npos) {
        token = s.substr (pos_start, pos_end - pos_start);
        pos_start = pos_end + delim_len;
        res.Push_back (token);
    }

    res.Push_back (s.substr (pos_start));
    return res;
}

int main() {
    string str = "adsf-+qwret-+nvfkbdsj-+orthdfjgh-+dfjrleih";
    string delimiter = "-+";
    vector<string> v = split (str, delimiter);

    for (auto i : v) cout << i << endl;

    return 0;
}


Ausgabe

 adsf 
 qwret 
 nvfkbdsj 
 orthdfjgh 
 dfjrleih 




Für Trennzeichen für einzelne Zeichen

Zeichenfolge basierend auf einem Zeichenbegrenzer aufgeteilt. B. die Zeichenfolge "adsf+qwer+poui+fdgh" mit dem Trennzeichen "+", wird {"adsf", "qwer", "poui", "fdg"h}.__ ausgegeben.

#include <iostream>
#include <sstream>
#include <vector>

using namespace std;

vector<string> split (const string &s, char delim) {
    vector<string> result;
    stringstream ss (s);
    string item;

    while (getline (ss, item, delim)) {
        result.Push_back (item);
    }

    return result;
}

int main() {
    string str = "adsf+qwer+poui+fdgh";
    vector<string> v = split (str, '+');

    for (auto i : v) cout << i << endl;

    return 0;
}


Ausgabe

 adsf 
 qwer 
 poui 
 fdgh 
9
Arafat Hasan

Dieser Code trennt Zeilen von Text und fügt alle zu einem Vektor hinzu.

vector<string> split(char *phrase, string delimiter){
    vector<string> list;
    string s = string(phrase);
    size_t pos = 0;
    string token;
    while ((pos = s.find(delimiter)) != string::npos) {
        token = s.substr(0, pos);
        list.Push_back(token);
        s.erase(0, pos + delimiter.length());
    }
    list.Push_back(s);
    return list;
}

Angerufen von: 

vector<string> listFilesMax = split(buffer, "\n");
9
William Cuervo

Ich würde boost::tokenizer verwenden. Hier ist eine Dokumentation, die erläutert, wie Sie eine geeignete Tokenizer-Funktion erstellen: http://www.boost.org/doc/libs/1_52_0/libs/tokenizer/tokenizerfunction.htm

Hier ist eines, das für Ihren Fall funktioniert.

struct my_tokenizer_func
{
    template<typename It>
    bool operator()(It& next, It end, std::string & tok)
    {
        if (next == end)
            return false;
        char const * del = ">=";
        auto pos = std::search(next, end, del, del + 2);
        tok.assign(next, pos);
        next = pos;
        if (next != end)
            std::advance(next, 2);
        return true;
    }

    void reset() {}
};

int main()
{
    std::string to_be_parsed = "1) one>=2) two>=3) three>=4) four";
    for (auto i : boost::tokenizer<my_tokenizer_func>(to_be_parsed))
        std::cout << i << '\n';
}
4

Hier ist meine Meinung dazu. Es behandelt die Edge-Fälle und benötigt einen optionalen Parameter, um leere Einträge aus den Ergebnissen zu entfernen.

bool endsWith(const std::string& s, const std::string& suffix)
{
    return s.size() >= suffix.size() &&
           s.substr(s.size() - suffix.size()) == suffix;
}

std::vector<std::string> split(const std::string& s, const std::string& delimiter, const bool& removeEmptyEntries = false)
{
    std::vector<std::string> tokens;

    for (size_t start = 0, end; start < s.length(); start = end + delimiter.length())
    {
         size_t position = s.find(delimiter, start);
         end = position != string::npos ? position : s.length();

         std::string token = s.substr(start, end - start);
         if (!removeEmptyEntries || !token.empty())
         {
             tokens.Push_back(token);
         }
    }

    if (!removeEmptyEntries &&
        (s.empty() || endsWith(s, delimiter)))
    {
        tokens.Push_back("");
    }

    return tokens;
}

Beispiele

split("a-b-c", "-"); // [3]("a","b","c")

split("a--c", "-"); // [3]("a","","c")

split("-b-", "-"); // [3]("","b","")

split("--c--", "-"); // [5]("","","c","","")

split("--c--", "-", true); // [1]("c")

split("a", "-"); // [1]("a")

split("", "-"); // [1]("")

split("", "-", true); // [0]()
3

Dies ist eine vollständige Methode, die die Zeichenfolge auf ein beliebiges Trennzeichen aufteilt und einen Vektor der zerlegten Zeichenfolgen zurückgibt.

Es ist eine Adaption aus der Antwort von RyanBwork. Seine Prüfung auf: if(token != mystring) führt jedoch zu falschen Ergebnissen, wenn Ihre Zeichenfolge sich wiederholende Elemente enthält. Dies ist meine Lösung für dieses Problem.

vector<string> Split(string mystring, string delimiter)
{
    vector<string> subStringList;
    string token;
    while (true)
    {
        size_t findfirst = mystring.find_first_of(delimiter);
        if (findfirst == string::npos) //find_first_of returns npos if it couldn't find the delimiter anymore
        {
            subStringList.Push_back(mystring); //Push back the final piece of mystring
            return subStringList;
        }
        token = mystring.substr(0, mystring.find_first_of(delimiter));
        mystring = mystring.substr(mystring.find_first_of(delimiter) + 1);
        subStringList.Push_back(token);
    }
    return subStringList;
}
2
Amber Elferink

Wenn Sie die Zeichenfolge nicht ändern möchten (wie in der Antwort von Vincenzo Pii) und auch das letzte Token ausgeben möchten, können Sie diesen Ansatz verwenden:

inline std::vector<std::string> splitString( const std::string &s, const std::string &delimiter ){
    std::vector<std::string> ret;
    size_t start = 0;
    size_t end = 0;
    size_t len = 0;
    std::string token;
    do{ end = s.find(delimiter,start); 
        len = end - start;
        token = s.substr(start, len);
        ret.emplace_back( token );
        start += len + delimiter.length();
        std::cout << token << std::endl;
    }while ( end != std::string::npos );
    return ret;
}
1
user2366975
#include<iostream>
#include<algorithm>
using namespace std;

int split_count(string str,char delimit){
return count(str.begin(),str.end(),delimit);
}

void split(string str,char delimit,string res[]){
int a=0,i=0;
while(a<str.size()){
res[i]=str.substr(a,str.find(delimit));
a+=res[i].size()+1;
i++;
}
}

int main(){

string a="abc.xyz.mno.def";
int x=split_count(a,'.')+1;
string res[x];
split(a,'.',res);

for(int i=0;i<x;i++)
cout<<res[i]<<endl;
  return 0;
}

PS: Funktioniert nur, wenn die Länge der Zeichenfolgen nach dem Teilen gleich ist

0
Sudhakar R

Die Antwort ist bereits vorhanden, bei der ausgewählten Antwort wird jedoch die Löschfunktion verwendet, die sehr kostspielig ist. Denken Sie an eine sehr große Zeichenfolge (in MB). Deshalb benutze ich untenstehende Funktion.

vector<string> split(const string& i_str, const string& i_delim)
{
    vector<string> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while(found != string::npos)
    {
        string temp(i_str.begin()+startIndex, i_str.begin()+found);
        result.Push_back(temp);
        startIndex = found + i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if(startIndex != i_str.size())
        result.Push_back(string(i_str.begin()+startIndex, i_str.end()));
    return result;      
}
0
Shubham Agrawal