Textdatei in Array einlesen (Trennzeichen)
-
Hallo,
ich möchte eine Textdatei in ein array einlesen. Ich habe dazu schon ein paar andere Threads gefunden und gelesen, allerdings passen diese noch nicht genau zu meinem Problem.
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
...Die Spalten stehen in ihrer Anzahl fest, die Zeilen würde ich gerne beim Programmstart auszählen lassen.
Ich stell mir das bisher ungefähr so vor:
#include <iostream> #include <fstream> using std::cout; using std::cerr; using std::endl; using std::ifstream; int main() { ifstream input("personen.txt"); if(!input) { cerr << "Datei wurde nicht gefunden." << endl; return EXIT_FAILURE; } // hier fehlt noch die variable Bestimmung der Zeilenanzahl // und das Trennzeichen Semikolon muss noch irgendwie beachtet werden int anz_zeilen; //irgendwie auslesen? String array_personen[5][anz_zeilen] for(int index = 0; input >> daten; ++index) { array_personen[index] = daten; } // array irgendwie weiter verarbeiten return 0; }
Geht das vllt. mit der Readline Methode oder so? Einige Methoden habe ich schon gelesen, aber mit Trennzeichen findet man kaum Beschreibungen.
Hoffe ihr könnt mir helfen.
Danke Gruß
-
Dieser Thread wurde von Moderator/in Martin Richter aus dem Forum MFC (Visual C++) in das Forum C++ (auch C++0x, bzw. C++11) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
std::getline gibt es. Aber warum nutzt du keinen std::vectorstd::string? Das wäre die perfekte Lösung für dich. Für die Daten solltest du vielleicht eine eigene Klasse anlegen.
-
Naja es ist eine Übungsaufgabe und da sind Vektoren untersagt, weil sie zu viele Funktionien bietet, die einem die Sache zu leicht machen.
Die ganze Aufgabe geht dann auch noch weiter, also das einlesen ist nur der erste Schritt. Deswegen habe ich gehofft, dass ihr mir einen Tipp geben könnt.
-
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?