Textdatei in Array einlesen (Trennzeichen)
-
tankwart schrieb:
Naja es ist eine Übungsaufgabe und da sind Vektoren untersagt, weil sie zu viele Funktionien bietet, die einem die Sache zu leicht machen.
Warum sind dann Streams erlaubt? Warum Strings? Warum sind dann überhaupt C++-Standard-Header erlaubt, wäre doch alles viel zu einfach.
Wo auch immer du diese Aufgabe her hast, der Erfinder ist doof.
-
tankwart schrieb:
Geht das vllt. mit der Readline Methode oder so? Einige Methoden habe ich schon gelesen, aber mit Trennzeichen findet man kaum Beschreibungen.
Hallo tankwart,
willkommen im C++-Forum.
Zum Lesen mit Trennzeichen findest Du hier im Forum schon einiges z.B. hier. Auf jeden Fall ist Dir eine eigene Klasse oder struct Person zu empfehlen, die einen eigenen Streaming-Operator bekommt ... (folge dem Link)
Gruß
Werner
-
Hallo,
ich habe mich jetzt selber noch ein wenig versucht.
Über readalllines habe ich nur ein Array mit folgendem Inhalt:
[Hans Wurst;Hauptstr. 8;12345;Schulzendorf;1,81m]
[Marie Lehmann;Dorfstr. 1;54321;Müllerdorf;1,57m]Gibt es nun eine Funktion (evtl. die String::Split-Methode?) um daraus ein folgendes zweidimensionales Array zu machen?
[Hans Wurst][Hauptstr. 8][12345][Schulzendorf][1,81m]
[Marie Lehmann][Dorfstr. 1][54321][Müllerdorf][1,57m]Ich weiß dass ihr mir eine andere Möglichkeit mit getline gezeigt habt, aber ich will es ja auch versuchen zu verstehen und somit alleine ein wenig weiterkommen und auch andere Möglichkeiten kennenlernen.
Danke, Gruß Jens
-
nein, so eine split methode gibt es nicht fertig.
dafür gibts so was fürs einlesen:
http://www.cplusplus.com/reference/string/getline/
wie man das verwendet, solltest du in werners beiträgen zur genüge finden.wenn du wirklich eine split methode brauchst(willst), könntest du dir aber eine bauen:
#include <string> #include <utility> std::pair<std::string, std::string> split_once(std::string in) //std::pair<std::string, std::string> split_once(const std::string& in) --- spart dir eine kopie - siehe übergabe per value/const reference { const char split_char = ';'; std::size_t split_pos = in.find(split_char); if(split_pos == std::string::npos) //split_char nicht enthalten return std::make_pair(in, ""); if(split_pos == 0) //leerer eintrag return std::make_pair("", in); std::string first( in.substr(0, split_pos-1); std::string second( in.substr(split_pos) ); //2. parameter ist optional -> bis zum ende return std::make_pair(first, second); }
oder (falls du schon referenzen hattest):
std::string split_inplace(std::string& in) { const char split_char = ';'; std::size_t split_pos = in.find(split_char); if(split_pos == std::string::npos) { std::string ret = in; in.clear(); return ret; /* oder die letzten drei zeilen so. ich weiß nicht, ob übliche compiler so besser optimieren können - ich nehme aber mal an, schon: std::string ret; swap(ret, in); return ret; */ } if(split_pos == 0) { in.pop_front(); return ""; } std::string ret = in.substr(0, split_pos-1); in = in.substr(split_pos); return ret; }
wenn du beide funktionen brauchst, wäre es aber schwachsinn, die normale split-funktion nicht über die inplace-funktion zu implementieren.
falls die anwendung nicht klar sein sollte:
int main() { std::string test = "asdf;ghj"; while( !test.empty() ) { std::pair<std::string, std::string> splitted = split_once(test); std::cout << splitted.first << std::endl; test = spliited.second; } // oder /*std::string*/ test = "asdf;ghj"; while( !test.empty() ) { std::string splitted = split_inplace(test); std::cout << splitted << std::endl; } }
natürlich kannst du das ';' auch direkt mit an die split-funktion übergeben, um die funktion evtl irgendwann mal wieder zu verwenden.
auch kannst du dir sehr einfach eine komplette split-funktion bauen, falls du irgendwann mal std::vector nutzen darfst (mal angenommen, du hast die split-funktion so verändert, dass man das trennzeichen mit übergibt):
#include <vector> #include "my_split_function.h" std::vector<std::string> split_all(std::string in, char delim) { std::vector<std::string> ret; while( !in.empty() ) { std::string to_add = split_inplace(in, delim); ret.push_back(to_add); //oder gleich in eine zeile: //ret.push_back( split_inplace(in, delim) ); } return ret; }
(jetzt bekommst du zum beispiel bei
eins;;drei
einen leeren string (-> eine leere zeile in das array). also nicht darauf verlassen, dass jeder string/jede zeile auch etwas enthält)bb
-
Erstmal vielen Dank für deine ausführliche Antwort.
Du hast natürlich Recht, wahrscheinlich ist es sinnvoller mit direkt mit getline einzulesen, um somit nach Trennzeichen zu splitten.Ich habe dazu folgenden Code von Werner Salomon gefunden:
#include <iostream> #include <fstream> #include <string> // -- Eine Person struct Person { std::string vname; std::string nname; std::string alter; std::string geschlecht; }; // -- diese Funktion liest eine Person vom std::istream std::istream& lese_ein( std::istream& in, Person& p ) { getline( in, p.vname, ';'); getline( in, p.nname, ';'); getline( in, p.alter, ';'); getline( in, p.geschlecht, '\n'); return in; } int main() { using namespace std; ifstream csvread( "test.csv" ); if(!csvread.is_open()) cerr << "Fehler beim Oeffnen der Datei!" << endl; else { for( Person person; lese_ein( csvread, person ); ) { ; // tue was mit 'person' } } return 0; }
Um das auf mein Beispiel anzupassen habe ich folgendes gemacht.
struct.h#include <string> struct Person { std::string vname; std::string nname; std::string alter; std::string geschlecht; }; std::istream& lese_ein( std::istream& in, Person& p ) { getline( in, p.vname, ';'); getline( in, p.nname, ';'); getline( in, p.alter, ';'); getline( in, p.geschlecht, '\n'); return in; }
main.cpp
#include "stdafx.h" #include "Form1.h" #include <fstream> #include <iostream> #include <string> int main(array<System::String ^> ^args) { ifstream datei("test.txt"); if(!datei.is_open()) { cerr << "Fehler beim Oeffnen der Datei" << endl; return -1; } // müsste nun hier im else Zweig die Funktion "lese_ein" aufgerufen werden? ... return 0; }
Form1.h
#include "struct.h" ... private: System::Void button_start_Click(System::Object^ sender, System::EventArgs^ e) { // hier sollte nun beispielsweise der Wert von "vname" in einer Textbox dargestellt werden // textbox->Text = Aufruf_der_Funktion }
Bin ich denn auf dem richtigen Weg?
-
Das ist kein C++, und schon langsam gehen mir die ganzen C++/CLI'ler auf den Wecker. Die M$-Idioten sollen mal aufhören ihren Scheiß als C++ zu bezeichnen -.-
-
314159265358979 schrieb:
Das ist kein C++, und schon langsam gehen mir die ganzen C++/CLI'ler auf den Wecker. Die M$-Idioten sollen mal aufhören ihren Scheiß als C++ zu bezeichnen -.-
Danke für diesen hilfreichen Beitrag ... wenn du genervt bist, okay, aber beleidigen musst du mich noch lange nicht dafür, auch wenn du mehr Ahnung von C++ hast.
-
314159265358979 schrieb:
Das ist kein C++, und schon langsam gehen mir die ganzen C++/CLI'ler auf den Wecker. Die M$-Idioten sollen mal aufhören ihren Scheiß als C++ zu bezeichnen -.-
Mal ne Frage: Warum ist das kein C++? Habs schon öfters gelesen, aber verstehe ich nicht, warum das kein C++ sein soll
-
tankwart schrieb:
aber beleidigen musst du mich noch lange nicht dafür, auch wenn du mehr Ahnung von C++ hast.
Wenn du dich bei "M$-Idiot" angesprochen fühlst, ist das dein Problem. Ich habe dich damit nicht gemeint.
Ikognito schrieb:
Mal ne Frage: Warum ist das kein C++? Habs schon öfters gelesen, aber verstehe ich nicht, warum das kein C++ sein soll
Es ist C++/CLI, eine Managed Sprache von M$.
-
Auch wenn das C++/CLI ist, kannst du mir ja vllt. trotzdem an der einen oder anderen Stelle weiterhelfen!?
-
tankwart schrieb:
Ich habe dazu folgenden Code von Werner Salomon gefunden:
...
Um das auf mein Beispiel anzupassen habe ich folgendes gemacht.
struct.h#include <string> struct Person { std::string vname; std::string nname; std::string alter; std::string geschlecht; }; std::istream& lese_ein( std::istream& in, Person& p ) ...
Bin ich denn auf dem richtigen Weg?
Im Prinzip ja, aber Du bist noch nicht weit gekommen. Die struct Person aus meinem Posting hat die Attribute Vorname, Nachname, Alter, Geschlecht. Aber die Person aus der von Dir angegebenen Datei
tankwart schrieb:
Ich habe eine Textdatei wo Daten (mit Semikolon getrennt) hinterlegt sind, die in ein zweidimensionales array eingelesen werden sollen. (kein Vektor)
Bsp.:
Hans Wurst;Hauptstr. 8;12345;Schulzendorf;1,81m
Marie Lehmann;Dorfstr. 1;54321;Müllerdorf;1,57m
...hat die Attribute Name, Straße, PLZ, Ort, Größe. Das solltest Du natürlich erst mal anpassen. Somit sieht die struct Person in Deinem Fall etwa so aus:
struct Person { std::string name; std::string strasse; std::string plz; std::string ort; double groesse_in_m; };
Das Einlesen ist entsprechend anzupassen. Achte darauf, dass die Größe vom Typ double ist. Das Zeichen 'm' (Meter) hinter der Größenangabe ist also zu überlesen. Schau Dir dazu mal die Char<>-Funktion aus diesen Beitrag an.
.. und warum eine 'lese_ein'-Funktion, wenn's der Streaming-Operator auch tut?
std::istream& operator>>( std::istream& in, Person& p )
:xmas2: Werner
-
Werner Salomon schrieb:
.. und warum eine 'lese_ein'-Funktion, wenn's der Streaming-Operator auch tut?
std::istream& operator>>( std::istream& in, Person& p )
:xmas2: Werner
Die "lese_ein"-Funktion splittet doch die Zeile genau bei dem Semikolon. Macht das der Streaming-Operator automatisch? Klar die Verknüpfung zur Struktur ist über das Objekt p gegeben, aber der eigentliche "Einleseprozess" würde doch wegfallen!?
-
tankwart schrieb:
Werner Salomon schrieb:
.. und warum eine 'lese_ein'-Funktion, wenn's der Streaming-Operator auch tut?
std::istream& operator>>( std::istream& in, Person& p )
:xmas2: Werner
Die "lese_ein"-Funktion splittet doch die Zeile genau bei dem Semikolon. Macht das der Streaming-Operator automatisch? Klar die Verknüpfung zur Struktur ist über das Objekt p gegeben, aber der eigentliche "Einleseprozess" würde doch wegfallen!?
er macht es automatisch, ja (wenn du es ihm beibringst^^).
und er ist nun mal wesentlich besser zu erkennen, als ein funktions-aufruf.bb
-
unskilled schrieb:
tankwart schrieb:
Werner Salomon schrieb:
.. und warum eine 'lese_ein'-Funktion, wenn's der Streaming-Operator auch tut?
std::istream& operator>>( std::istream& in, Person& p )
:xmas2: Werner
Die "lese_ein"-Funktion splittet doch die Zeile genau bei dem Semikolon. Macht das der Streaming-Operator automatisch? Klar die Verknüpfung zur Struktur ist über das Objekt p gegeben, aber der eigentliche "Einleseprozess" würde doch wegfallen!?
er macht es automatisch, ja (wenn du es ihm beibringst^^).
und er ist nun mal wesentlich besser zu erkennen, als ein funktions-aufruf.bb
Was bedeutet denn "Wenn ich es ihm beibringe"?
Ich muss ihm das Trennzeichen mitübergeben oder wie?
-
Du sollst den Operator selber schreiben! Und dabei darfste mit den Trennzeichen machen, wozu du lustig bist.
-
SeppJ schrieb:
Du sollst den Operator selber schreiben! Und dabei darfste mit den Trennzeichen machen, wozu du lustig bist.
Also ist es genau das Gleiche, wie die "Lese_ein"-Funktion? Wo ist dann der Vorteil?
-
tankwart schrieb:
SeppJ schrieb:
Du sollst den Operator selber schreiben! Und dabei darfste mit den Trennzeichen machen, wozu du lustig bist.
Also ist es genau das Gleiche, wie die "Lese_ein"-Funktion? Wo ist dann der Vorteil?
Du darfst die Funktion auch gerne
sdk4ht34i5heiddhe9c43h
nennen, dann weiß eben auch niemand, was sie tut und wie sie zu benutzen ist und sie arbeitet nicht mit den Mitteln der Standardbibliothek (z.B. Stream-Iteratoren) zusammen. Oder du kannst dich an existierende und durchdachte Konventionen halten.
-
Mir geht eher sowas auf den Senkel:
cerr << "Fehler beim Oeffnen der Datei" << endl;
Was spricht dagegen, das Problem anzuzeigen (zB Datei gesperrt, Datei existiert nicht) ?
-
Scheppertreiber schrieb:
Mir geht eher sowas auf den Senkel:
cerr << "Fehler beim Oeffnen der Datei" << endl;
Was spricht dagegen, das Problem anzuzeigen (zB Datei gesperrt, Datei existiert nicht) ?
Dagegen spricht, dass diese Information bei einem std::fstream schlicht nicht vorliegt. Die Fehlerbehandlung der Standard-IO-Library ist da eben sehr einfach gestrickt.
Ein Versuch wäre noch
ifstream file; file.exceptions( ios_base::failbit | ios_base::badbit ); file.open( "nicht-vorhanden.txt" );
was dann im Text der Exception steht, ist aber ausschließlich von der Implementierung der IO-Library abhängig. Bei MS kommt da auch nur:
ios_base::failbit set
also kein bisschen mehr an Info.
Bleibt noch der Rückfall in die C-Fehlerbehandlung - frag 'errno' ab. Wobei auch hier meines Wissens nicht garantiert ist, dass das gesetzt wird.
:xmas2: Werner