String bzw. Char 'splitten'
-
Hallo @ all,
ich habe leider das "kleine" Problem das ich es einfach nicht schaffe einen Text zu splitten und anschließend in die Klassen zu transportieren.
Meine Frage wäre mal vorne weg, ist es einfacher bzw. besser einen char zu splitten oder einen string?
void TravelAgency::readFile() { // SAMPLE: F|70|1499.0|20150104|20150109|FRA|ORD|United Airlines fstream datei; char zeile[256]; string stringzeile; datei.open("bookings.txt", ios::in); string trennung_("|"); while (!datei.eof()) { datei.getline(zeile, sizeof(zeile)); stringzeile = charToString(zeile); // die Attribute in den Klassen sind entweder ein String oder Integer.. cout << stringzeile << endl; } cout << "Textdatei erfolgreich eingelesen" << endl; datei.close(); } string TravelAgency::charToString(char toString[]){ stringstream ss; string isString; ss << toString; ss >> isString; return isString; }
Wenn ich es nur hinkriegen würde anhand des Zeichens ('|') den String bzw. Charkette zu splitten wäre der Rest nun ein Kinderspiel.
Falls möglich nicht explizit die Lösung verraten sondern ich würde es gerne selber versuchen, damit es auch hängen bleibt
Danke im Vorrauuuuuus
-
Naja, du brauchst halt eine split-Hilfsfunktion, welche die Trennzeichen sucht und dann die einzelnen Strings extrahiert und in einen vector o.ä. stopft. Auf einzelne Zeichen greifst du mit operator[] zu (kannst auch find() benutzen), einen Teilstring erstellst du mit substr().
-
Darüber hinaus sieht das Einlesen doch ein wenig merkwürdig aus. Die Abbruchbedingung ist jedenfalls nicht richtig (getline kann fehlschlagen) und das mit 256 recht willkürlich dimensionierte Array sieht auch suspekt aus. Soweit mir bekannt macht man zeilenweises Einlesen in C++ üblicherweise so:
ifstream datei("bookings.txt"); string zeile; while (getline(datei,zeile)) { cout << zeile << endl; } cout << "Textdatei erfolgreich eingelesen" << endl;
-
Mit getline kann man auch splitten:
fstream datei("bookings.txt"); string data; while (getline(datei, data, '|')) { cout << data << '|'; } cout << "Textdatei erfolgreich eingelesen" << endl;
Und am besten mal ein paar Beiträge von "Werner Salomon" hier im Forum anschauen, z.B. CSS ähnliche Text-Datei parsen oder [Hilfe] .csv zu .xml.
-
Ich habe es mal etwas verbessert nur komme ich in eine Endlosschleife.
void TravelAgency::readFile() { ifstream data("bookings.txt"); string line; while (getline(data, line)) { TravelAgency::splitter(line); } } void TravelAgency::splitter(string line_){ string symbol ("|"); size_t pos; string substring; int length_; while(line_ != "\n"){ // Solange line_ nicht gleich "Enter" ist length_ = line_.length(); // Länge des Strings pos = line_.find(symbol); // Finde Position vom Symbol substring = line_.substr(0,pos); // Anfang bis Position = Substring line_ = line_.substr(pos+1, length_);// line_ ist ab nun ab der Position bis zum Ende der "Zeile" cout << substring << endl; } }
Textdokument:
F|70|1499.0|20150104|20150109|FRA|ORD|United Airlines R|71|466.0|20150104|20150109|Chicago OHare Intl Airport|Chicago OHare Intl Airport|Avis H|72|501.03|20150104|20150109|Mark Twain Hotel|Peoria F|73|1358.0|20150109|20150110|ORD|FRA|United Airlines F|74|330.0|20150608|20150608|STR|CDG|Air France
Meine Ausgabe:
United Airlines United Airlines ...
Mein Ziel war es immer bis zum Symbol einen substring zu "erstellen" und den Rest - natürlich ohne das Symbol am Anfang - als neue line_ zu haben, damit er den so lange zerlegt bis ich nur noch das "Enter" habe.
Ich hoffe das mein Ansatz überhaupt richtig ist
Danke bisher an alle die mir geholfen haben
-
Deine Abbruchbedingung stimmt nicht. Der Fall das in
line_
nur noch ein Newline enthält kommt nie vor. Das Newline hatgetline
schon entfernt. Du müsstest mal einen Test einbauen ob deinfind(symbol)
überhaupt noch was findet, bevor du den Anfang des Strings löschen willst. Wennfind()
nichts findet gibt es einen sehr großen Wert (string::npos) zurück. Dieser Wert +1 ist zu groß für den Datentyp, es gibt einen Overflow und wir sind wieder bei 0.
-
Hallo depream,
depream schrieb:
Meine Frage wäre mal vorne weg, ist es einfacher bzw. besser einen char zu splitten oder einen string?
am besten weder noch!
Was vielen Leuten nicht bewusst ist; es gibt in C++ einen std::istream in Form z.B. von cin oder ifstream. Und die Aufgabe dieses Streams ist es NICHT(nur) Zeichen aus einer Datei in den Speicher zu schaufeln - das könnte ein std::streambuf auch ganz allein, sondern aus einer Folge von Zeichen ein Objekt zu machen. Ein Objekt kann ein Zeichen, ein Integer, ein Datum oder eine TravelAgency sein. Die Folge von Zeichen kann in einer Datei oder im Memory (als String) stehen. Es bleibt eine Folge von Zeichen und das Lesen einer kompletten Zeile aus der Datei in einen String verschiebt das Problem nur und löst praktisch nichts (ok - nicht viel, aber dazu vielleicht später).
Th69 hat das schon vorgemacht. Wobei ich noch einen Schritte weiter gehe und nicht nur Strings lesen, sondern gleich die Objekte.
Um z.B. ein Zeichen oder ein Integer zu lesen gibt es Funktionen in C++.
ifstream datei(...); char c; // ein Zeichen datei >> c; // liest das Zeichen if( datei ) { // alles ok; usw.
In 'c' kann jetzt ein 'F' ein 'R' oder ein 'H' oder was anderes stehen.
Für das lesen (und prüfen) des Trennzeichens hält C++ auch etwas bereit. Zunächst wie oben:char pipe_zeichen; datei >> pipe_zeichen; if( datei && pipe_zeichen == '|' ) { // erst jetzt ist alles i.O.
Ein std::istream kann auch eine Funktion 'aufnehmen', die eine gewisse Manipulation vornimmt. Man nennt diese Funktion daher auch Manipulator. Sie muss folgende Signatur haben:
std::istream& 'name'( std::istream& );
'name' ist irgendein Name natürlich ohne ''. Angenommen es gibt einen Manipulator
pipe
- also:std::istream& pipe( std::istream& );
dann kann man folgendes aufrufen:
datei >> pipe;
Hier wird diese Funktion dann mit 'datei' als Parameter aufgerufen und die Implementierung von
pipe
muss eine Referenz auf 'datei' wieder zurückgeben.std::istream& pipe( std::istream& ) { char pipe_zeichen; in >> pipe_zeichen; if( in && pipe_zeichen == '|' ) return in; // alles gut in.setstate( std::ios_base::failbit ); // setzt das Fehlerbit im istream return in; }
eine fortgeschrittene Variante und weitere Tipps findest Du als Char<> hier.
Um die ersten beiden Objekte (Zeichen und Nummer) zu lesen reicht dann:
char c; int nr; datei >> c >> pipe >> nr; if( datei ) { // Erfolg
depream schrieb:
Falls möglich nicht explizit die Lösung verraten sondern ich würde es gerne selber versuchen, damit es auch hängen bleibt
dann versuche es mal selber weiter.
Noch eine Frage: In der 3. Zeile der Textdatei ist ein '|' weniger als in den anderen 4 Zeilen. Ist das ein Fehler oder können hinter dem zweiten Datum 'beliebig' viele Felder und damit '|'-Zeichen pro Datensatz kommen?
Gruß
Werner