String in Teilstrings aufteilen
-
Hallo,
ich kann kaum glauben, dass ich diesen Beitrag gerade schreibe, denn die betreffende Problemstellung erscheint schon fast trivial simpel. Doch die Lösungsfindung lässt mich langsam an meinem Verstand zweifeln.
Die Situation ist folgende:
Der Konstruktor einer Klasse bekommt als einzigen Parameter einen String übergeben, welcher aus einer festen Anzahl Teilstrings (6) mit ebenfalls festem Trennzeichen(' ') besteht.
Ein String-Array soll nun mit diesen Teilstrings gefüllt werden, damit diese anschließend einzeln verarbeitet werden können.Es sei der Vollständigkeit halber noch gesagt, dass die Länge der einzelnen Teilstrings variabel ist, weshalb ein einfacher Iterator hier nicht als Lösung in Frage kommt.
-
Wie wär's mit boost?
#include <boost/algorithm/string.hpp> ... vector<string> teilstrings; boost::split(teilstrings, "hier dein String", boost::is_any_of(" "), boost::token_compress_off);
Ansonsten kannst du mit string::find auch einfach das jeweils nächste Leerzeichen suchen und das Splitten seber machen.
-
wob schrieb:
Wie wär's mit boost?
Ich bin kein Freund von boost. Wenn eben möglich möchte ich darauf verzichten.
wob schrieb:
Ansonsten kannst du mit string::find auch einfach das jeweils nächste Leerzeichen suchen und das Splitten seber machen.
Das wäre schon eher was ich suche. Ein kleines Beispiel zur Veranschaulichung wäre nett.
Da außerdem die Anzahl der Teilstrings ja bekannt ist, sehe ich keinen Grund Vektoren einzusetzen. Ein einfaches Array mit sechs feldern ist hier völlig ausreichend.
-
Warum packst du den String nicht in einen StringStream und liest direkt wieder aus.
-
kommt darauf an, wie genau du das verwenden willst. welche art von strings? welche art von arrays? welche art von performance ist wichtig? wie stabil muss das interface sein?
da wäre alles möglich, von boost::split über std::stringstream bis hin zu std::strtok.
hier ein beispiel, das nichts braucht, außer std::string und eine eigene klasse (die das splitting übernimmt, weil du ja ein beispiel wolltest). gehe davon aus, dass du mit "string" immer std::string und mit "array" immer std::array meinst.
struct splitter { size_t pos = 0; string const& src; static constexpr char delim = ' '; template <class Str> splitter (Str const& src) : src{src} {} string operator() () { size_t len = src.find_first_of(delim, pos+1) - pos; string retval { src.substr(pos, len) }; pos += len+1; return retval; } }; struct Foo { array<string, 6> substrs; explicit Foo (splitter split) : substrs{split(), split(), split(), split(), split(), split()} { } }; //verwendung: Foo f {"1abc 2de 3fghijk 4lmn 5opq 6rstuvwxyz"s};
-
Du kannst auch sowas wie strtok nehmen, wenn du es lieber C-styliger haben willst. Siehe Beispiel auf http://www.cplusplus.com/reference/cstring/strtok/
Oder mach aus deinem String einen Stringstream und nimm lies einfach daraus.
Oder suche einfach mal ein bisschen und finde zum Beispiel: http://stackoverflow.com/questions/236129/split-a-string-in-c
PS: was ist denn der Vorteil deines Arrays gegenüber dem vector (also jetzt hier für deinen Fall)? Brauchst du überhaupt ein Array oder kannst du die gesplitteren Strings nicht auch direkt verarbeiten?
-
wer den fehler findet, darf ihn gern ausbessern.
-
Braunstein schrieb:
Warum packst du den String nicht in einen StringStream und liest direkt wieder aus.
Auch das habe ich schon versucht. Die Trennzeichen zu finden, sei es mit std::string::find() oder mit hilfe eines StringStreams ist nicht das Problem. Es ist das anschließende aufspalten des Strings die Teilstrings, das die Probleme bereitet.
kommt darauf an, wie genau du das verwenden willst. welche art von strings? welche art von arrays? welche art von performance ist wichtig? wie stabil muss das interface sein?
Stabilität und vor allem Performance sind hier äußerst wichtig.
Ich verwende std::string und ein ganz normales array.... using namespace std; ... string tmp[6];
dove schrieb:
struct splitter { size_t pos = 0; string const& src; static constexpr char delim = ' '; template <class Str> splitter (Str const& src) : src{src} {} string operator() () { size_t len = src.find_first_of(delim, pos+1) - pos; string retval { src.substr(pos, len) }; pos += len+1; return retval; } }; struct Foo { array<string, 6> substrs; explicit Foo (splitter split) : substrs{split(), split(), split(), split(), split(), split()} { } }; //verwendung: Foo f {"1abc 2de 3fghijk 4lmn 5opq 6rstuvwxyz"s};
Muss da denn wirklich so ein enormer Aufwand betrieben werden? Ich will doch nichts weiter als ein ganz normales Array mit den Teilstrings eines großen Strings füllen. Das muss doch auch ohne eine zusätzliche Klasse und Datenstrukturen möglich sein.
wob schrieb:
Du kannst auch sowas wie strtok nehmen, wenn du es lieber C-styliger haben willst. Siehe Beispiel auf http://www.cplusplus.com/reference/cstring/strtok/
strtok ist C und nicht C++. Ich halte C und C++ immer strikt von einander getrennt, was in diesem Projekt auch eine absolute Notwendigkeit ist.
wob schrieb:
Oder mach aus deinem String einen Stringstream und nimm lies einfach daraus.
Wie schon gesagt wäre hier ein Beispiel schön.
wob schrieb:
Oder suche einfach mal ein bisschen und finde zum Beispiel: http://stackoverflow.com/questions/236129/split-a-string-in-c
C und C++. s.o.
wob schrieb:
PS: was ist denn der Vorteil deines Arrays gegenüber dem vector (also jetzt hier für deinen Fall)? Brauchst du überhaupt ein Array oder kannst du die gesplitteren Strings nicht auch direkt verarbeiten?
Wenn die Anzahl der Elemente bekannt ist, brauche ich einfach keinen Listentyp, der sich dynamisch vergrößern kann. Außerdem benötigen Vektoren generell mehr RAM-Speicher als einfache Arrays und sind auch alles andere als effizient, wenn sie zusätzlichen Speicher anfordern.
-
damit gibst du immerhin schon mehr information.
eine sehr einfache implementierung wäre:#include <sstream> #include <algorithm> #include <string> using namespace std; struct Bar { string substrs[6]; Bar(string const& str) { const char delim = ' '; istringstream stream{str}; for_each(substrs, substrs+6, [&stream, delim](string& str) { getline(stream, str, delim); }); } };
falls du kannst, würde ich an deiner stelle trotzdem std::array anstelle von einem plain-array verwenden.
hier hast du "nur" den nachteil, strings umsonst zu initialisieren. und ein riesengeschütz (stringstream) für eine einfache aufgabe aufzufahren. deinen hinweisen zufolge ist das aber irrelevant.
-
ZaHaDum1984 schrieb:
wob schrieb:
Oder mach aus deinem String einen Stringstream und nimm lies einfach daraus.
Wie schon gesagt wäre hier ein Beispiel schön.
wob schrieb:
Oder suche einfach mal ein bisschen und finde zum Beispiel: http://stackoverflow.com/questions/236129/split-a-string-in-c
C und C++. s.o.
Du musst dem Link, den du ja sogar zitierst, auch folgen. Da sind wirklich genügend Beispiele drin.
Was meinst du mit "C und C++ s.o."?
-
whoops, jetzt hab ich ja glatt "performance" überlesen. du weißt: "premature optimization" usw., aber nach ein paar messungen und je nachdem, wie wichtig dir das tatsächlich ist, solltest du dir vielleicht doch eine eigene helfer-klasse schreiben und auf stringstreams verzichten.
kurz mal mit ein paar kleinen (!) strings gemessen ist die lösung, die ich zuerst gepostet habe ca. 6 mal schneller als die lösung mit stringstream (kein unterschied zwischen std::strings und c-strings).
hier der code:
//splitter für std::strings struct splitter { size_t pos = 0; string const& src; static constexpr char delim = ' '; splitter (string const& src) : src{src} {} string next () { size_t len = src.find_first_of(delim, pos+1) - pos; string retval { src.substr(pos, len) }; pos += len+1; return retval; } array<string, 6> create() { array<string, 6> tmp { this->next(), this->next(), this->next(), this->next(), this->next(), this->next() }; return tmp; } }; //splitter für c-strings using zstring = char const*; struct csplitter { zstring str; static constexpr char delim = ' '; csplitter (zstring str) : str{str} {} string next () { zstring end = strchr(str, delim); if (end == nullptr) return string{str}; string retval { str, end }; str = end+1; return retval; } array<string, 6> create () { array<string, 6> tmp { this->next(), this->next(), this->next(), this->next(), this->next(), this->next() }; return tmp; } }; //mit eigener klasse, einmal c-strings, einmal std::strings struct Foo { array<string, 6> substrs; explicit Foo (string const& str) : substrs{splitter{str}.create()} { } explicit Foo (char const* str) : substrs{csplitter{str}.create()} { } }; //mit stringstream struct Bar { string substrs[6]; Bar(string const& str) { istringstream stream{str}; const char delim = ','; for_each(substrs, substrs+6, [&stream, delim](string& str) { getline(stream, str, delim); }); } }; int main () { string a = "1abdecde 2fdsd 3fghijk 4lmn 5opq 6rstuvwxyz"; //char const* a = "1abdecde 2fdsd 3fghijk 4lmn 5opq 6rstuvwxyz"; string b = "1abcade 2fdsd 3fghijk 4eelmn 5opq 6rstudxvwxyz"; //char const* b = "1abcade 2fdsd 3fghijk 4eelmn 5opq 6rstudxvwxyz"; for (int i = 0; i < 10'000'000; ++i) { //Foo f {rand()%2? a : b}; Bar b {rand()%2? a : b}; } }
-
das ganze wird im übrigen nochmal um eine größenordnung (!) schneller, wenn man statt ein array aus std::strings ein array aus std::experimental::string_view verwendet, was vermutlich ausreicht (ohne den genauen anwendungsfall zu kennen).
-
Der Anwendungsfall ist das parsen vin Forsyth-Edwards Notationen.
Ich habe mich für folgende Lösung entschieden:
Position::Position(string FEN) { string tmp[6], element; stringstream ss(FEN); unsigned int i = 0; while (getline(ss, element, ' ')) { tmp[i] = element; i++; } ... }
-
dove schrieb:
ein array aus std::experimental::string_view
Was ist das genau?
-
im prinzip ein wrapper rund um einen std::string ODER einen c-string (mit/ohne '\0'), eigentlich also genau das richtige für einen sub-string. so in der art ein c-string mit allen funktionen eines std::strings, nur ohne gleich den heap zu verwenden. das heißt natürlich, dass ein string_view immer auf ein elternobjekt verweisen muss, das auch existiert.
string_view foo () { string bar = "quux"; return string_view{bar}; //BOOOM! }
ab 2017 teil von C++.