Daten aus txt datei in Klasse einlesen



  • @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.



  • Und mit Vector meinte ich

    string Zeilen[5];
    

    Sorry, falsch zugeordnet in meinem Kopf, die Grundfragestellung würde aber die gleiche bleiben

    LG



  • Du mußt ersteinmal alle deine Personendaten in einem std::vector<Person> speichern (s. Code von @Swordfish und @Tyrdal).
    Erst dann kannst du mittels einer Suchfunktion anhand eines Namens (oder anderen Attributen) dadrin suchen.



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

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

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

    Die Beispieldatei sähe so aus:

    Na wie jetzt? Wie ist ein Record genau aufgebaut?
    Und müssen die Anführungszeichen wirklich sein? Das macht das ganze bloß unnötig komplifiziert.



  • @Swordfish Mit einer passenden Definition von Nummern stimmt das eh mit dem Beispiel überein 😆
    (Aber klar, wenn man mit Nummern meint dass da "1" "2" "42" drin steht, dann sollte man das dazuschreiben.)



  • Sorry, da habt ihr recht. ich nehme die " " raus, dann wird´s wohl einfacher.
    Leider muss ich ehrlich gesagt zugeben, dass ich durch die Codes nicht ganz durchblicke.
    Ich habe auch nicht den Anspruch, dass alles möglichst schön und effizient ist. Gibt es denn irgendwie die Möglichkeit, auf meinem bestehenden Code aufzubauen und das eingelesene in eine Klasse zu packen oder einen Vector (wobei ich dachte, dass ein Vector nur aus Zahlen besteht, berichtigt mich bitte, wenn ich falsch liege)

    LG



  • @PardiMau
    Der Einstiegswiderstand bei der STL ist relativ hoch, da Vieles auf abstrakten Konzepten beruht und als Einsteiger nicht intuitiv zu verstehen ist. Da muss man sich schon Mal ein Buch schnappen und etwas lesen. Sobald man aber Templates und Iteratoren halbwegs verstanden hat ergibt plötzlich alles Sinn.
    Ein std::vector ist ein Klassentemplate, da kann man fast alles reinstecken, nicht nur Zahlen. Man kann ihn auch problemlos in einer Funktion erzeugen und als Rückgabewert zurückgeben. Damit bietet sich das Einlesen in einer eigenen Funktion ja gerade an. Und nicht alles muss als Methode einer Klasse implementiert werden, gerade das Einlesen aus einer Datei würde ich in deinem Fall als freie Funktion implementieren.
    Darfst du das Dateiformat frei wählen oder ist es vorgegeben?



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

    Gibt es denn irgendwie die Möglichkeit, auf meinem bestehenden Code aufzubauen und das eingelesene in eine Klasse zu packen oder einen Vector

    Sicherlich.

    Als erstes bastelst du dir jetzt mal eine struct Person die die Daten aufnehmen soll.
    Dann geht's ans Einlesen.

    Nimm diese Variante die du hattest:

    	while (true)
    	{
    		for (auto& i : Zeilen)
    			getline(in, i);
    		if (!in)
    			break; // Es konnte keine Datei gefunden werden (sic)
    		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) // (sic)
    			break; //Ende der fünfzeiligen Ausgabe (sic)
    	}
    

    An der Stelle if (i = 5) hast du jetzt die 5 Zeilen eines Eintrags in Zeilen[0] ... Zeilen[4] stehen.
    Statt dem if (i = 5) machst du dort jetzt erstmal eine Variable vom Typ Person hin und befüllst deren Felder.
    Die "Listen" Felder (Eltern, Kinder) vergisst du erstmal, um die können wir uns später kümmern - immer schön der Reihe nacht.

    Zur Kontrolle ob das alles funktioniert hat baust du dir dann erstmal eine Ausgabe dazu - also einfach die Person Variable die du gerade befüllt hast ausgeben. Pack die Ausgabe am besten in eine eigene Unterfunktion, dann bleibt das übersichtlicher und die Funktion kannst du dann später auch einfach wiederverwenden.

    Dann nimmst du dir den Teil vor alles in einen vector<Person> zu packen. Das ist relativ easy, einfach vor der Schleife eine Variable mit Typ vector<Person> anlegen und in der Schleife die lokale Person Variable in den vector reinstopfen.

    Dann nimmst du die Ausgabe aus der Einleseschleife raus und machst nach der Einleseschleife eine Ausgabeschleife wo du bloss alles wieder ausgibst was du erst eingelesen hast.

    Dann bist du schonmal ein gutes Stück weit gekommen.

    Dann kannst du dir aussuchen was du als nächstes angehst: Suche einer bestimmten Person im vector oder erstmal das was wir auf die lange Bank geschoben hatten implementieren (Einlesen der Listen-Felder).

    Dazu meldest du dich einfach nochmal hier.
    Bzw. auch gerne wenn du bei einem der Schritte davor nicht weiterkommst.



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

    if (i = 5)

    ist immer wahr. if (i == 5)?


Anmelden zum Antworten