Daten aus txt datei in Klasse einlesen



  • Hallo, ich bin noch recht neu bei C++ und habe folgendes Problem.

    Ich habe eine Textdatei und will Daten in eine Klasse einlesen. Die Grobe Struktur wäre:

    Nummer
    Name
    Nummer
    Nummern
    Nummern

    Dann geht es quasi mit der nächsten Zeile wieder mit der ersten Nummer los. Ist leider mit Zeilenumbrüchen. Das ist mein Problem.
    Kann mir da irgendwer weiterhelfen?

    LG



  • @PardiMau Was hast du schon und woran genau hapert's?



  • @Tyrdal

    Bislang habe ich mir die Datei ausgeben lassen können
    und die klasse für mich aufgeschrieben, wie sie eingeteilt werden soll

    #include "pch.h"
    #include <iostream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
    	//Datei einlesen
    	ifstream in("Datei.txt");
    	if (in.is_open())
    	{
    		string Zeile;
    		while (getline(in, Zeile))
    		{
    			cout << Zeile << endl;
    		}
    		in.close();
    	}
    	else
    	{
    		cout << "Datei konnte nicht geöffnet werden" << endl;
    	}
    
    	// Implementierung der gewünschten Klasse 
    	class Person
    	{
    	public:
    		unsigned int Nummer;
    		string Name;
    		unsigned int Geburtsjahr;
    		unsigned int Eltern;
    		unsigned int Kind;
    	};
    }
    

    Mein Ziel ist es vorerst, einen Namen einzugeben und dann den gesamten zugehörigen fünfzeiligen Datensatz herausgeben zu lassen.
    Ich weiß nur nicht, wie man die jeweils 5 aufeinanderfolgenden Zeilen in eine Klasse packt, falls das überhaupt das richtige Mittel ist.


  • Gesperrt

    Hi, Mein Vorschlag wäre, dass du es Zeile für Zeile auslesen lässt. Dann hast du nicht 5 Zeilen zusammen, sondern 5 separate strings.
    LG Gwen



  • @cchip sagte in Daten aus txt datei in Klasse einlesen:

    Hi, Mein Vorschlag wäre, dass du es Zeile für Zeile auslesen lässt.

    Macht er ja schon. Das ist nicht das Problem. Hast du die Frage überhaupt verstanden? Und hast du seinen letzten Beitrag gelesen?



  • @cchip sagte in Daten aus txt datei in Klasse einlesen:

    5 separate strings

    Und dann? Wieder in einen Stream stopfen und extrahieren was man *wirklich* haben will?? Nene.



  • @PardiMau sagte in Daten aus txt datei in Klasse einlesen:

    Ich weiß nur nicht, wie man die jeweils 5 aufeinanderfolgenden Zeilen in eine Klasse packt, falls das überhaupt das richtige Mittel ist.

    Kann man auf jeden Fall so machen, ich würde da jetzt erstmal nix falsches daran sehen.

    Und wie man das macht? Naja du solltest nicht Zeile für Zeile sondern Datensatz für Datensatz einlesen:

    		string Zeilen[5];
    		while (true)
    		{
    			for (auto& z : Zeilen)
    				getline(in, z);
    			if (!in)
    				break; // Es konnte kein vollständiger Datensatz mehr gelesen werden
    			cout << "Z1: " << Zeilen[0] << endl;
    			cout << "Z2: " << Zeilen[1] << endl;
    			cout << "Z3: " << Zeilen[2] << endl;
    			cout << "Z4: " << Zeilen[3] << endl;
    			cout << "Z5: " << Zeilen[4] << endl;
    		}
    

    Statt Zeilen[0] ... Zeilen[4] auszugeben kannst du die dann in deine Klasse packen.

    Mein Ziel ist es vorerst, einen Namen einzugeben und dann den gesamten zugehörigen fünfzeiligen Datensatz herausgeben zu lassen.

    In dem Fall kannst du auch den Namen direkt in der Schleife vergleichen und die Daten dann 1:1 ausgeben -- ohne dass du erst alles in eine grosse Liste (vector) packst.



  • @Swordfish sagte in Daten aus txt datei in Klasse einlesen:

    @cchip sagte in Daten aus txt datei in Klasse einlesen:

    5 separate strings

    Und dann? Wieder in einen Stream stopfen und extrahieren was man *wirklich* haben will?? Nene.

    Das ganze Zeilenweise einzulesen ist schon nicht so doof.
    Speziell wenn Leerzeichen enthalten sein können ist es anders extrem fummelig.

    Zeig mal Code wie du ohne getline sowas wie Hans Peter Mustermann oder 1 2 3 4 5 einlesen willst, so dass trotzdem der Zeilenumbruch als "hier ist jetzt schluss" Markierung erkannt wird. Und dann können wir diskutieren ob der Code für einen Anfänger geeignet ist 🙂



  • @hustbaer sagte in Daten aus txt datei in Klasse einlesen:

    Zeig mal Code wie du ohne getline

    Ich glaube da gab es ein Missverständnis.

    #include <cstdlib>
    #include <vector>
    #include <string>
    #include <sstream>
    #include <iostream>
    #include <fstream>
    
    class person_t
    {
        unsigned id;
        std::string name;
        unsigned year_of_birth;
        unsigned mother;
        unsigned father;
        std::vector<unsigned> children;
    
    public:
        void pretty_print(std::ostream &os = std::cout) const
        {
            os << "ID: " << id << "\nName: " << name << "\nYear: " << year_of_birth
               << "\nMother: " << mother << "\nFather: " << father << "\nChildren: ";
            for (auto child : children)
                os << child << ' ';
        }
    
        friend std::istream& operator>>(std::istream &is, person_t &person)
        {
            person_t tmp;
            std::string children;
            if (!((is >> tmp.id) && std::getline(is, tmp.name) && (is >> tmp.year_of_birth) &&
                  (is >> tmp.mother) && (is >> tmp.father) && std::getline(is, children)))
            {
                return is;
            }
        
            std::istringstream iss{ children };
            for (unsigned child_id; iss >> child_id;)
                tmp.children.push_back(child_id);
            
            person = tmp;
            return is;
        }
    
        friend std::ostream& operator<<(std::ostream &os, person_t const &person)
        {
            os << person.id << '\n' << person.name << '\n' << person.year_of_birth << '\n' << person.mother << '\n' << person.father << '\n';
            for (auto child : person.children)
                os << child << ' ';
            os.put('\n');
            return os;
        }
    };
    
    int main()
    {
        auto filename = "Datei.txt";
        std::ifstream is{ filename };
        if (!is.is_open()) {
            std::cerr << "Couldn't open \"" << filename << "\" for reading :(\n\n";
            return EXIT_FAILURE;
        }
    
        std::vector<person_t> persons{ std::istream_iterator<person_t>{ is }, std::istream_iterator<person_t>{} };
        for (auto const &person : persons)
            person.pretty_print();
    }
    


  • Also abgesehen davon dass dein Code Probleme bekommt wenn nicht genau zwei Eltern erfasst sind... OK.
    Ist aber vermutlich auch nicht für nen Anfänger geeignet.

    Aber @PardiMau kann uns ja sagen ob er damit was anfangen kann.



  • ps: Und wenn's dir um Effizienz geht, dann solltest du vermeiden für jeden Datensatz einen neuen stringstream zu erzeugen. Und das selbe natürlich mit dem temp-string. Geht natürlich nicht mit dem schicken operator >>. MMn. braucht man den schicken operator >> aber auch nicht unbedingt 🙂



  • @hustbaer sagte in Daten aus txt datei in Klasse einlesen:

    ps: Und wenn's dir um Effizienz geht, dann solltest du vermeiden für jeden Datensatz einen neuen stringstream zu erzeugen.

    Sieh's mal so: Ich habe einen sstream pro Record anstatt der vorgeschlagenen 6 oder 7. Und überhaupt - bevor man das nicht vermessen hat ...

    @hustbaer sagte in Daten aus txt datei in Klasse einlesen:

    Geht natürlich nicht mit dem schicken operator >>.

    Geht sicher. Aber viel Aufwand und wahrscheinlich nicht mehr hübsch. Sexy comes in all sizes though.



  • @Swordfish sagte in Daten aus txt datei in Klasse einlesen:

    Sieh's mal so: Ich habe einen sstream pro Record anstatt der vorgeschlagenen 6 oder 7.

    Der Vorschlag war 5 strings. Von 5 stringstreams war nie die Rede. Genaugenommen war überhaupt nie die Rede von irgendwelchen stringstreams.

    Und überhaupt - bevor man das nicht vermessen hat ...

    Ich dachte dass deine implizite Kritik sich gerade auf die schlechte Performance bezogen hat.

    @hustbaer sagte in Daten aus txt datei in Klasse einlesen:

    Geht natürlich nicht mit dem schicken operator >>.

    Geht sicher. Aber viel Aufwand und wahrscheinlich nicht mehr hübsch.

    Ich hab ja auch geschrieben geht nicht mit dem schicken operator >>. Natürlich kann man immer irgendeine Kranke Ausgeburt der Hölle bauen.

    Sexy comes in all sizes though.

    Nein. Ganz sicher nicht 🙂



  • @hustbaer sagte in Daten aus txt datei in Klasse einlesen:

    Der Vorschlag war 5 strings. Von 5 stringstreams war nie die Rede.

    Ich verdränge immer erfolgreich daß es auch andere Möglichkeiten gibt Zahlen aus strings zu kratzen 😞



  • Ich kann dich verstehen.
    Ich verdränge immer erfolgreich dass man blöd rumcasten muss wenn man nen char nach isdigit & Co reinfüttern will.
    (Ja, OK, ist ein anderes Thema, aber ich hab das wirklich schon vielfach erfolgreich verdrängt.)

    BTW: Ein Hoch auf from_chars. Endlich vernünftige Integer-/Float-Parser die man sich nicht selbst schreiben muss.



  • Etwas weniger 90ies style als Swordfish wäre meine Lösung (hier etwas vereinfacht, weil kein try/catch usw.) so ähnlich:

    #include <fstream>
    #include <iostream>
    #include <string>
    #include <vector>
    
    using std::stoi;
    
    struct Person {
      unsigned int Nummer;
      std::string Name;
      unsigned int Geburtsjahr;
      unsigned int Eltern;
      unsigned int Kind;
    };
    using Persons = std::vector<Person>;
    
    auto readFile() {
      Persons persons;
    
      std::ifstream in("Datei.txt");
      if (!in.is_open()) {
        // error handling, maybe throw
      }
    
      std::string lines[5];
    
      while (true) {
        for (auto &line : lines) {
          getline(in, line);
        }
        if (!in) {
          break;
        }
    
        persons.emplace_back(
            Person{static_cast<unsigned int>(stoi(lines[0])), lines[1],
                   static_cast<unsigned int>(std::stoi(lines[2])),
                   static_cast<unsigned int>(std::stoi(lines[3])),
                   static_cast<unsigned int>(std::stoi(lines[4]))});
      }
    
      return persons;
    }
    
    int main() {
      auto persons = readFile();
    
      for (auto const &person : persons) {
        std::cout << person.Nummer << " : " << person.Name << " : "
                  << person.Geburtsjahr << " : " << person.Eltern << " : "
                  << person.Kind << "\n";
      }
    }
    

    PS: Ich finde cin furchtbar. Da richtige Fehlerbehandlung hinzubekommen ist die Hölle.

    int i=0;
    cin >> i;
    

    Tasächliche Eingabe: "Nö"



  • Danke für eure bisherigen Antworten!

    Ich bin jetzt mit dem Ansatz von @hustbaer so weit gekommen, dass wenn ich eine Zahl eingebe, mir auch ein einzelner Datensatz (also 5 Zeilen) ausgegeben wird.

    int main()
    {
    	//Datei einlesen
    	ifstream in("Datei.txt");
    
    	string Zeilen[5];
    
    	int i;
    
    	cin >> i;
    	cout << "\n";
    
    	while (true)
    	{
    		for (auto& i : Zeilen)
    			getline(in, i);
    		if (!in)
    			break; // Es konnte keine Datei gefunden werden
    		cout << setw(20) << "Nummer: " << Zeilen[0] << endl;
    		//cout << setw(20) << "Name: " << Zeilen[1] << endl;
    		cout << setw(20) << "Geburtsjahr: " << Zeilen[2] << endl;
    		cout << setw(20) << "Eltern: "  << Zeilen[3] << endl;
    		cout << setw(20) << "Kinder: " << Zeilen[4] << endl;
    
    		if (i = 5)
    			break; //Ende der fünfzeiligen Ausgabe
    	}
    

    Das Programm reagiert so, dass der unterste fünfzeilige Eintrag die Null ist, dann wird hochgezählt, der oberste Eintrag hat also die höchste Zahl.
    Das soll mir recht sein, ist ja nur wichtig, dass ich es weiß.
    Jetzt will Ich einen Namen eingeben können und den zugehörigen Datensatz bekommen.

    Mache ich das mit:

    string Datenbank = "Datei.txt";
    	
    	string Name;
    	cin >> Name;
    
    	Datenbank.find(Name);
    	
    	Name = i;
    

    oder bin ich da komplett auf dem Holzweg?

    LG



  • Oder kann ich vielleicht sogar direkt den zweiten Eintrag des Vektors eingeben lassen, um den kompletten Vektor ausgeben zu lassen?

    (Was wohl für die Benutzerfreundlichkeit nicht gerade gut wäre, weil die Eingabe, dann komplett fehlerfrei sein muss, aber vielleicht lässt sich das im nachhinein noch fixen)



  • Also zumindest mal ist dein i komisch.
    Du hast nämlich 2 is, einmal die Zahl, die du einliest, und einmal die z-Variable von @hustbaer, die du offenbar umbenannt hast. Das verwirrt doch! Mach sowas nicht! (das z stand für zeile)

    Und das hier:
    if (i = 5)
    ist eine Zuweisung, kein Vergleich! Ich weiß nicht so recht, was du da erreichen willst. Ich habe keine Ahnung, was du mit "Das Programm reagiert so, dass der unterste fünfzeilige Eintrag die Null ist, dann wird hochgezählt, der oberste Eintrag hat also die höchste Zahl." meinst/sagen willst.

    Der Compiler kann bei solchen Dingen übrigens warnen. Schalte alle Warnungen ein und behebe dann die Warnungen!

    Ich verstehe gerade nicht, was du mit dem i überhaupt erreichen willst. Du redest auch von vector-Einträgen, ich sehe aber gar keinen vector hier.



  • @wob

    oh ja, danke, da ist einiges, was ich nicht brauch,
    nur break reicht vollkommen.
    (sonst wäre es eine endlosliste geworden 😅 )

    das eine i ist jetzt auch wieder ein z, auch wenn alles vorher ohne Fehlermeldung funktioniert hat.

    Ich brauche ein i, da ich ja nicht alle Einträge ausgeben will, sondern nur einen.

    Die Beispieldatei sähe so aus:

    0001
    Franz Müller
    1996
    "0004" "0005"
    "0019" "0020"
    0002
    Helga Richter
    1994
    "0041" "0042"
    "0019" "0020"
    0003
    Sabine Müller
    1990
    "0004" "0005"
    "8" "9"
    0004
    Sandra Müller
    1967
    "10" "11"
    "0001" "0003"
    0005
    Peter Müller
    1966
    "12" "13"
    "0001" "0003"

    Wenn ich die 0 eingebe, dann gibt mir das Programm
    0005
    Peter Müller
    1966
    "12" "13"
    "0001" "0003"

    Wenn ich die 1 eingebe, dann
    Sandra Müller
    1967
    "10" "11"
    "0001" "0003"
    usw.

    Ich will aber Peter Müller eingeben und dann den Datensatz bekommen, der zu Peter Müller gehört.


Anmelden zum Antworten