.txt Datei auslesen klappt nicht richtig



  • Hallo,
    weis jetz nich ob das hier richtig ist.

    Hab vor drei mit c++ Tagen angefangen und komme jetzt nicht weiter.

    Ich möchte mit dieser Funktion unter anderem eine .txt Datei auslesen, die ich vorher "gefüllt" habe. In dieser Datei stehen "Kundendaten", die pro Kunde 60 Zeichen lang sind.
    Die ersten beiden Zeichen geben die länge des Namens aus.
    Um zu prüfen ob das überhaupt funktioniert habe ich mir die länge der ersten beiden Namen ausgeben lassen. Jetzt habe ich das Problem, dass der erste Wert stimmt, der vom zweiten Kunden aber nicht und komm nicht weiter.

    Bsp:

    Txt-Datei:

    060401000032Kundea010120001000000000000000000000000000000000
    070401000033Kundeab01012000100000000000000000000000000000000

    Ausgabe Programm:

    _
    060401000032Kundea010120001000000000000000000000000000000000
    070401000033Kundeab01012000100000000000000000000000000000000

    06

    0
    _

    Eigentlich müsste es sogar zweimal "07" ausgegeben werden, weil ich es einmal als char und einmal als string versucht habe.
    Ich hab wirklich keinen Plan was dadran falsch ist. 😞

    Hier der Code:

    (StringConv::toString() konvertiert den char zu einem String)

    void Verwaltung::PrüfNeuerKunde(int KN, string Name, int GT, int GM, int GJ, long KS, short NKS)
    {
    	char c;
    	string daten;
    	ifstream f;
    	f.open("Kundenliste.txt");
    	if (!f)
       {
    	  getchar();
          cout << "Die Datei kann nicht geoeffnet werden.\n";
    	  getchar();
          exit(1);
       }
    		while (f.get(c))
       {
    	   daten = daten + StringConv::toString(c);
    	   cout << c;
       }
    
    	f.close();
    
    	int i = 0;
    
    	cout << daten << endl;
    	cout << daten[i] << daten[i+1] << endl;;
    
    	i = i + 60;
    	char a = daten[i];
    	char b = daten[i+1];
    
    	cout << a;
    	cout << b;
    
    	string fdaten = StringConv::toString(a) + StringConv::toString(b);
    
    	cout << fdaten;
    
    	getchar();
    }
    


  • char c; 
        string daten; // Variablen zu früh deklariert
        ifstream f; 
        f.open("Kundenliste.txt"); // Nutze den Konstruktor
        if (!f) // Hier prüfst du mit is_open()
       { 
          // Die ganze Fehlerbehandlung ist völlig falsch
          getchar();
          cout << "Die Datei kann nicht geoeffnet werden.\n"; 
          getchar(); 
          exit(1); 
       }
    

    Besser:

    ifstream f{"Kundenliste.txt"};
        if ( !f.is_open() )
    		throw std::runtime_error{"Die Datei kann nicht geoeffnet werden.\n"}; // Hattest du schon Exceptions?
    

    ________________________________________________________________________________________________________________________

    char c; 
       string daten; 
    
       while (f.get(c))
       {
           daten = daten + StringConv::toString(c); // Kürzer wäre: daten += c;
           cout << c;
       }
    

    Die Schleife ist furchtbar umständlich.
    Was du willst, ist die gesamte Datei in einen String lesen. Und das geht so:

    std::string daten{ std::istreambuf_iterator<char>{f}, std::istreambuf_iterator<char>{} };
    

    ________________________________________________________________________________________________________________________

    int i = 0;
    
        cout << daten << endl; // Wieso endl? Nimm '\n'
        cout << daten[i] << daten[i+1] << endl;;
    
        i = i + 60; // Hier wäre 1. der +=-Operator angebracht, 2. Solltest du eher nach dem newline suchen.
        char a = daten[i];
        char b = daten[i+1];
    
        cout << a;
        cout << b;
    

    besser:

    cout << daten << '\n';
    	cout << daten[0] << daten[1] << '\n';
    
    	auto i = daten.find('\n');
    	if( i != std::string::npos )
    		std::cout << daten[i+1] << daten[i+2];
    

    ______________________________________________________________________________________________________________

    string fdaten = StringConv::toString(a) + StringConv::toString(b);
    

    ➡

    string fdaten{a, b};
    

    Ich verstehe, dass ich dir als Anfänger eigentlich zuviel zumute. Falls du was nicht verstehst: Es gibt für alle "Lösungen" auch einfachere Varianten. Falls du Exceptions nicht kennst, lasse deine Funktion eine Art Fehler-Code zurückgeben. Und falls dein Compiler bei dem {}-Syntax (genannt list-initialization) meckert, kannst du den String aus a und b auch anders erzeugen.



  • Hi,

    beschreib mal den Aufbau deiner Textdatei genauer:
    -Was bedeutet welches Zeichen?
    -Welches Zeichen hat welchen Datentyp?
    Hast du den Aufbau selbst festgelegt? Falls ja, wie wärs mit ein paar Trennzeichen?

    Ein Kunde stellt man sich doch am besten als Objekt vor, oder nicht? Also wie wärs mit einer Klasse Kunde?



  • Dieses Beispielprogramm erzeugt das gewünschte Verhalten:

    #include <iostream>
    #include <sstream>
    #include <iterator>
    
    int main()
    {
    	using namespace std;
    
    	istringstream f{
    "060401000032Kundea010120001000000000000000000000000000000000\n\
    070401000033Kundeab01012000100000000000000000000000000000000" };
    
    	string daten{ istreambuf_iterator<char>{f}, istreambuf_iterator<char>{} };
    
    	cout << daten << "\n\n";
        cout << daten[0] << daten[1] << '\n';
    
        auto i = daten.find('\n');
        if( i != string::npos )
            cout << daten[i+1] << daten[i+2];
    }
    

    Aber wie out bereits ansprach, hat dein Format einige Macken. Allen voran: Keine Trennzeichen. Es ist viel einfacher, die C++-IOStreams die ganze Parserei übernehmen zu lassen, und das geht nochmal einfacher wenn du Whitespaces zwischen die einzelnen Zahlen setzt. Solange dein Dateiformat nicht absolut auf Größe hin optimiert werden muss, ist das verbesserbar.



  • Sone schrieb:

    Dieses Beispielprogramm erzeugt das gewünschte Verhalten:
    [code="cpp"]
    string daten{ istreambuf_iterator<char>{f}, istreambuf_iterator<char>{} };

    😃 😃 So kann man most vexing auch ausschalten. Auf die Idee bin ich noch net gekommen. 👍



  • Sone schrieb:

    Hattest du schon Exceptions?

    Nein noch nicht, deshalb hab ichs ja erstmal so gemacht 🙂

    Sone schrieb:

    Die Schleife ist furchtbar umständlich.
    Was du willst, ist die gesamte Datei in einen String lesen. Und das geht so:

    std::string daten{ std::istreambuf_iterator<char>{f}, std::istreambuf_iterator<char>{} };
    

    Ja, ich hab mich auch gefragt obs i wie einfacher geht. Danke, das kannte ich noch nicht. 😃

    Sone schrieb:

    cout << daten << '\n';
    	cout << daten[0] << daten[1] << '\n';
     
    	auto i = daten.find('\n');
    	if( i != std::string::npos )
    		std::cout << daten[i+1] << daten[i+2];
    

    ich gehe mal davon aus, dass daten.find die erste Stelle ausgibt, an der '\n' das erste mal vorkommt. Was ist aber, wenn ich die nächste Zeile danach haben will ?

    Sonst hab ich glaub ich alles verstanden.

    out schrieb:

    Hi,

    beschreib mal den Aufbau deiner Textdatei genauer:
    -Was bedeutet welches Zeichen?
    -Welches Zeichen hat welchen Datentyp?
    Hast du den Aufbau selbst festgelegt? Falls ja, wie wärs mit ein paar Trennzeichen?

    060401000032Kundea010120001000000000000000000000000000000000

    06 = Länge des Namen
    04 = Länge des Kontostandes(vor dem Komma)
    01 = Länge des Kontostandes(nach dem Komma)
    000032 = Kundennummer (immer 6-stellig)
    Kundea = Vor- und Nachname
    01 = Geburtstag
    01 = Geburtsmonat
    2000 = Geburtsjahr
    1000 = Kontostand
    0 = Kontostand (Nach dem Komma)

    der Rest wird mit Nullen aufgefüllt, damit die Länge immer gleich ist (60 Zeichen)

    Ja Trennzeichen wären gut, dann könnte man sich das mit den jeweiligen Längen sparen. 🙂
    Ich kenn halt nur noch nicht so viel Funktionen und hab versucht das beste aus dem zu machen was ich schon weis. 🙂

    out schrieb:

    Ein Kunde stellt man sich doch am besten als Objekt vor, oder nicht? Also wie wärs mit einer Klasse Kunde?

    Ja daran hab ich hinterher auch gedacht. Wie mach ich das dann wenn ich das einer anderen Funktion übergeben will? Kann ich dann die ganze Klasse Kunde übergeben oder wie?



  • Das ist ne schöne Übungsaufgabe für Anfänger. Allerdings glaube ich, für dich ist noch etwas zu hoch gegriffen. Ich würde die Übungsaufgabe so lösen:

    #include <fstream>
    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    struct kunde
    {
    	string kundennummer;
    	string vorname;
    	string nachname;
    	string geburtstag;
    	string geburtsmonat;
    	string geburtsjahr;
    	int kontostand; // In Cent
    };
    
    double cent_in_euro( int cent )
    {
    	return cent/100.0;
    }
    
    string geburtsdatum( const string& geburtstag, const string& geburtsmonat, const string& geburtsjahr )
    {
    	return geburtstag + '.' + geburtsmonat + '.' + geburtsjahr;
    }
    
    istream& operator>>( istream& in, kunde& k ) // Liest 1 Kunde ein.
    {
    	in  >> k.kundennummer
    		>> k.vorname
    		>> k.nachname
    		>> k.geburtstag
    		>> k.geburtsmonat
    		>> k.geburtsjahr
    		>> k.kontostand;
    	return in;
    }
    
    ostream& operator<<( ostream& out, const kunde& k ) // Gibt 1 Kunde aus.
    {
    	out << "Kundennummer: " << k.kundennummer << '\n';
    	out << "Name        : " << k.vorname      << ' ' << k.nachname << '\n';
    	out << "Geburtsdatum: " << geburtsdatum(k.geburtstag,k.geburtsmonat,k.geburtsjahr) << '\n';
    	out << "Kontostand  : " << cent_in_euro(k.kontostand) << " Euro" << '\n';
    	return out;
    }
    
    int main()
    {
    	/*
    		test.txt:
    			000032 Hans Peter 01 02 2000 1234
    			000033 Paul Mayer 06 08 1999 5678
    	*/
    	ifstream file( "test.txt" );
    
    	vector<kunde> kunden; // std::vector ist DER Standardcontainer. Er verwaltet all deine Kunden.
    
    	for(kunde k; file>>k; kunden.push_back(k)); // Alle Kunden einlesen.
    
    	for(const auto& k : kunden) // Testausgaben: Alle Kunden ausgeben.
    		cout << k << '\n';
    }
    

    Kannst ja mal versuchen, das ganze hier nachzuvollziehen. Wenn es zu schwierig ist, musst du wohl einfachere Aufgaben suchen bzw. dir ein gutes Anfängerbuch zulegen, da sind dann auch passende Aufgaben drin.



  • Kommt jetz vielleicht n bisschen spät 🙂

    Also Vectoren und Structs hatte ich noch nicht so richtig 🙂 Die sollte ich mir dann wohl so langsam mal angucken. Aber sonst verstehe ich glaube ich alles.

    Danke nochmal für deine Antwort 🙂



  • MuTo schrieb:

    Also Vectoren und Structs hatte ich noch nicht so richtig 🙂

    Du hattest schon Klassen, aber noch keien Structs?!
    Eine struct ist eine class, wo standardmäßig alles public ist.


Anmelden zum Antworten