Accountinformation aus Textdatei herauslesen



  • Hallo,
    Ich komm bei meinem Problem einfach nicht weiter und zwar versuche ich ein Script für die Passwortabfrage zu erstellen.

    Nun möchte ich den Benutzernamen,das Passwort und Informationen die diesem Account zugeordnet sind aus einer text datei herauslesen

    Beispiel:

    Account:Max Mustermann
    Passwort:1234567
    Information: Max Mustermann ist sehr begabt.

    Account:Franz Frech
    Passwort:909089
    Information: Franz Frech hat eine eigene Firma.

    Nun möchte ich wenn ich als Accountnamen Max Mustermann eingebe,dass er das dazugehörige passwort anfordert und wenn das Passwort richtig ist Die dazugehörige Information preisgibt.

    Ich möchte auch nich immer 1. 2. 3. 4. 5. Zeile etc schreiben müssen sondern ich will ein allgemeins Script um auch neu angelegte Benutzer nicht noch einmal definieren zu müssen

    Ebenfalls würde ich gerne das Passwort ändern können d.h genau den teil in der Textdatei überschreiben der vorher das passwort angegeben hat.

    Nun habe ich aber keine Ahnung wie ich da ran gehen soll das größe Problem scheint mir die datei genau da auszulesen wo ich es haben will 😛

    Ich hab auch schon ein vorgefertigtes Script ist aber glaub zulang ums hier zu posten 🙂

    Wär schön wenn mir jemand helfen könnte.

    Mfg Simon


  • Mod

    Lad die Daten einmal und halte sie im Speicher. Wenn es derart viele Daten sein sollten, dass dies unrealistisch ist, dann suchst du ein professionelles Datenbanksystem.

    Xyriez schrieb:

    Ebenfalls würde ich gerne das Passwort ändern können d.h genau den teil in der Textdatei überschreiben der vorher das passwort angegeben hat.

    Hashes sind doch sowieso alle gleich lang. Du speicherst doch nicht wirklich das Passwort, oder? 😉



  • Hm eigentlich nicht nein^^ aber wie lade ich die hashes am anfang in den Speicher? kannst mir vielleicht ma ein beispiel geben ? ( bin blutiger anfänger^^ )


  • Mod

    Xyriez schrieb:

    Hm eigentlich nicht nein^^ aber wie lade ich die hashes am anfang in den Speicher? kannst mir vielleicht ma ein beispiel geben ? ( bin blutiger anfänger^^ )

    Weißt du, wie du überhaupt irgendetwas aus einer Datei bekommst?



  • Also ich hab des bis jetzt mit ifstream gemacht

    main()
    {
    ifstream lesen_aus_datei;
    string dateiname_lesen="test.txt";

    lesen_aus_datei.open(dateiname_lesen.c_str(),ios::in);

    if(!lesen_aus_datei)
    {
    cout<<"Datei konnte nicht geöffnet werden!"<<endl;

    getchar();
    return -1;

    }

    char zeichen;

    while (!lesen_aus_datei.eof())

    {
    lesen_aus_datei.get(zeichen);
    cout<<zeichen;

    }

    lesen_aus_datei.close();
    getchar();


  • Mod

    ifstream lesen_aus_datei; 
    string dateiname_lesen="test.txt"; 
    
    lesen_aus_datei.open(dateiname_lesen.c_str(),ios::in);
    

    Redundanz pur. Schreibe einfach

    std::ifstream lesen_aus_datei("test.txt"); // Das in-Flag ist überflüssig
    
    char zeichen; 
    
    while (!lesen_aus_datei.eof()) 
    { 
        lesen_aus_datei.get(zeichen); 
        cout<<zeichen; 
    }
    

    Eine verpönte Weiterlaufbedingung.

    char zeichen; 
    while( lesen_aus_datei.get(zeichen) ) 
        cout << zeichen;
    

    Und das close() am Ende macht sowieso der Destruktor von ifstream automatisch. Also überflüssig.

    Nebenbei - wenn du eine Datei ausgeben willst, schreibe einfach (ungetestet)

    std::ifstream stream("Datei.txt");
    std::copy( std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(std::cout) );
    

  • Mod

    Nun hast du also Zeichen aus einer Datei gelesen und gleich danach wieder ausgegeben. Sind dir irgendwelche Techniken bekannt, mit denen du den Inhalt der Datei hättest speichern können? Lies mal die ganze Datei in eine einzige Datenstruktur. Dann gib sie danach aus.



  • Hallo Simon,

    Willkommen im C++-Forum.

    Xyriez schrieb:

    Nun möchte ich wenn ich als Accountnamen Max Mustermann eingebe,dass er das dazugehörige passwort anfordert und wenn das Passwort richtig ist Die dazugehörige Information preisgibt.

    Das erfordert schon relativ viel Wissen über C++, um das zu lösen, und ist als Anfänger-Aufgabe ungeeignet.

    Xyriez schrieb:

    Nun habe ich aber keine Ahnung wie ich da ran gehen soll das größe Problem scheint mir die datei genau da auszulesen wo ich es haben will 😛

    Am besten, Du lädst den gesamten Inhalt der Datei, bearbeitest ihn ggf. und schreibst ihn später am Stück zurück. Wenn Du es nur stückchenweise machen willst, ist eine Datenbank besser geeignet.

    Xyriez schrieb:

    Ich hab auch schon ein vorgefertigtes Script ist aber glaub zulang ums hier zu posten

    .. soviel ist das auch nicht. Anbei ein Programm, welches den ersten Teil Deiner Anforderungen erfüllt. Verstehen wirst Du es wahrscheinlich nicht - erklären will ich es Dir im Vorfeld auch nicht, dazu ist es jetzt schon zu spät am Tag.

    #include <iostream>
    #include <map>
    #include <string>
    #include <fstream>
    
    struct get_content // ein Helferlein zum Lesen von "keywort: Inhalt"
    {
        get_content( const std::string& key, std::string& content )
            : key_( &key )
            , content_( content )
        {}
        const std::string* key_; 
        std::string& content_;
    };
    std::istream& operator>>( std::istream& in, get_content x )
    {
        std::string key;
        if( getline( in >> std::ws, key, ':' ) && key != *x.key_ )
            in.setstate( std::ios_base::failbit );
        return getline( in >> std::ws, x.content_ );
    }
    
    struct Entry // ein Eintrag in der Datenbank
    {
        std::string pw_;
        std::string info_;
    };
    namespace
    {
        const std::string ACCOUNT = "Account";
        const std::string PASSWORT = "Passwort";
        const std::string INFO = "Information";
    }
    std::istream& operator>>( std::istream& in, Entry& e )
    {
        return in >> get_content( PASSWORT, e.pw_ ) >> get_content( INFO, e.info_ );
    }
    
    // diese Funktion liest den Inhalt der Datei in die 'map'
    std::map< std::string, Entry > read_database( const char* filename )
    {
        using namespace std;
        map< string, Entry > database;
        ifstream in( filename );
        if( !in.is_open() )
        {
            cerr << "Fehler beim Oeffnen von " << filename << endl;
            return database;
        }
        string account;
        for( Entry e;  in >> get_content( ACCOUNT, account ) >> e; )
            database[account] = e;
        if( !in.eof() )
        {
            cerr << "Fehler beim Lesen in " << filename << endl;
            return map< string, Entry >(); // mit leerem Container zurück
        }
        return database; // ok!
    
    }
    
    // und hier das Hauptprogramm: ruft 'read_database', fragt nach Account und PW und gibt dann die Info aus
    int main()
    {
        using namespace std;
        map< string, Entry > database = read_database( "input.txt" );
        if( database.empty() )
            return -2; // da ging was schief
        cout << "Bitte mit 'x' wie eXit beenden .." << endl;
        for( string account; cout << ACCOUNT << ": ", getline( cin >> ws, account ) && account != "x"; cout << "\n" )
        {
            auto i = database.find( account );
            if( i == end(database) )
            {
                cerr << ACCOUNT << ": " << account << " ist unbekannt" << endl;
                continue;
            }
            Entry& e = i->second;
            string passwort;
            cout << PASSWORT << ": ";
            if( cin >> passwort && passwort == e.pw_ )
            {
                cout << INFO << ": " << e.info_ << endl;
            }
            else
                cerr << "falsches Passwort!" << endl;
        }
        return 0;
    }
    

    Am besten Du stellst konkrete Fragen dazu.

    Gruß
    Werner



  • #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main() {
    
    ifstream input;  // (Objekt wurde noch nicht mit einer Datei assoziiert)
    //oder
    //ifstream input("insert_datei_name");  <-- oder gleich assoziieren.
    
    input.open("insert_datei_name");
    
    if (input.is_open()) {
    
       while(input.good()) {
    
       //make file operations
       //e.g. with getline und anschliessendem String parsen
    
        }
    } else { //Behandlung wenn Datei nicht geoeffnet werden konnte
      }   
    
    input.close();
    // Falls nachdem arbeiten mit der Datei im selben Scope noch etwas passiert,
    // was nichts mit der Datei zu tun hat, solltest du die Datei (nach meiner
    // Meinung) selbst schliessen. Ich persoenlich tue dies immer manuell.
    
    return 0;
    

    }

    Ich bin ein grosser Fan von http://www.cplusplus.com/reference

    Good prueft gleichzeitig 3 "stream flags" die man auch einzeln pruefen kann, eines ist das Ende der Datei.

    Quelle: http://www.cplusplus.com/reference/ios/ios/good/

    Es gibt mehrere Moeglichkeiten.

    1. Du sagst erstmal die Reihenfolge deiner Werte in der Datei sind immer fix und du setzt eine gewisse Syntax voraus.

    Dann haettest du so ein Format.
    Max Mustermann
    1234567
    Max Mustermann ist sehr begabt.

    Mit std::getline(); holst du die Information raus und packst sie in einen String. Du weisst alle 3 getlines gibt es einen neuen User. Du laedst dann d

    Der Nachteil ist, wenn ein User kommt und manuell in der Text Datei rumfummelt und die Reihenfolge aendert oder einfach eine Leerzeile pro Nutzer einfuegt stimmt es nicht mehr.

    Eine weitere Moeglichkeit waere den String, so wie du ihn jetzt hast, pro Zeile einzulesen und ihn dann zu parsen.

    Du solltest es erstmal so rudimentaer wie moeglich halten.
    Passwort in Klartext pruefen und erstmal die Nutzer im Speicher halten. (Wie Werner es geschrieben hat).


  • Mod

    Ruvi, deine Dateileselogik ist falsch. Erst prüfst du (unnötig umständlich mittels good), dann liest du, dann verarbeitest du. Was, wenn beim Lesen ein Fehler auftrat? Dann verarbeitest du ungeprüft Müll. Werner Salomon zeigt schon, wie Leselogik richtig geht, z.B. Zeile 52, 53: Lesen (hier: operator >>), dann verarbeiten (Rückgabewert prüfen), dann Verarbeiten (Zeile 53).



  • SeppJ schrieb:

    Ruvi, deine Dateileselogik ist falsch. Erst prüfst du (unnötig umständlich mittels good), dann liest du, dann verarbeitest du. Was, wenn beim Lesen ein Fehler auftrat? Dann verarbeitest du ungeprüft Müll. Werner Salomon zeigt schon, wie Leselogik richtig geht, z.B. Zeile 52, 53: Lesen (hier: operator >>), dann verarbeiten (Rückgabewert prüfen), dann Verarbeiten (Zeile 53).

    Danke,(ich lerne noch) wenn ich mich recht erinnere hatte ich dieses "Scheme" aus einem Lehrbuch und habe es seit dem verwendet ohne es hinterher gross in Frage zu stellen.

    Ich haette aber noch eine Frage was Werners code angeht.
    Nach SeppJ Anmerkung habe ich ihn mehr nochmal richtig angeguckt.

    Ich habe bis jetzt nur c++ code von mir gesehen und der ist bei weitem nicht so komplex.
    Ich bin ehrlich gesagt auch ein wenig geschockt.
    Ich habe jetzt an die 30-40min gebraucht um die 90 Zeilen Code von Werner im Detail zu verstehen und nachzuvollziehen.

    Aber sieht so, die Komplexitaet von c++ code (Schreibstil)in der Realitaet aus und ihr braucht mit den Augen nur einmal rueberfliegen und koennt alles verstehen?


  • Mod

    Ruvi schrieb:

    Danke,(ich lerne noch) wenn ich mich recht erinnere hatte ich dieses "Scheme" aus einem Lehrbuch und habe es seit dem verwendet ohne es hinterher gross in Frage zu stellen.

    Ein sicheres Zeichen, dass das Lehrbuch schlecht war.

    Zu Werners Code: Der ist schon durchaus eher auf der unverständlicheren Seite ('Tschuldigung Werner, aber das ist einfach so. Das weißt du auch selber). Werner ist einer der wenigen hier im Forum, die sich richtig gut mit den IOStreams auskennen und das sieht man dem Code auch an. Ich kann das recht flüssig lesen, da ich solche Codes selber schon einmal geschrieben habe und daher die typischen Muster kenne. Ich kann mir aber schon vorstellen, dass man, wenn man diesen Stil nicht gewöhnt ist, sehr große Verständnisschwierigkeiten hat.
    Im Kern steht da zweimal die typische C++-Leseschleife (aber die richtige, nicht die Version aus deinem Lehrbuch), einmal für die Datenbank:

    Containertyp container;
    Elementtyp element;
    while (eingabestream.lesen(element)) container.einfügen(element);
    

    und einmal für die Nutzereingaben:

    Eingabetyp eingabe;
    while (eingabestream.lesen(eingabe)) verarbeite(eingabe);
    

    Hier mittels einer for-Schleife umgesetzt, um den Gültigkeitsbereich der Variablen klein zu halten.
    Wenn man das erkannt hat, dann ist der Rest (insbesondere Zeile 6-40) die Angabe, wie die Leseaktion genau auszusehen hat. Diese ist ein bisschen verschachtelt. In der untersten Ebene stehen ein paar der bekannten, elementaren Operationen (An die Forenklugscheißer: Ja, operator>>(istream &, string&) ist nicht ganz elementar, aber für die Erklärung hier praktisch schon), in Zeilen 18 und 20. Dann kommen mehrere Schichten Logik drumherum, die machen, dass diese einfachen Operationen das hier beschriebene Format korrekt lesen.


  • Mod

    Aber sieht so, die Komplexitaet von c++ code (Schreibstil)in der Realitaet aus und ihr braucht mit den Augen nur einmal rueberfliegen und koennt alles verstehen?

    Ja, genau so ist es. Allerdings ist einmal "überfliegen" etwas übertrieben. Ich zum Beispiel habe etwa zwei Minuten gebraucht, um die Funktionsweise komplett zu verstehen.
    Das wird bei dir auch kommen, schließlich ist der Code eigentlich einfach, und du wirst die Elemente viel schneller (wieder)erkennen.

    Ich bin ein wenig überrascht, dass read_database den Dateinamen nimmt, statt eine Referenz auf istream . Und das die "Fehlerbehandlung" per cerr stattfindet.
    Vorschlag:

    std::map< std::string, Entry > read_database( std::istream& stream )
    {
    	using namespace std;
    	map<string, Entry> database;
    
    	pair<string, Entry> p;
    	while( stream >> get_content(ACCOUNT, p.first) >> p.second )
    		database.emplace( move(p) ); // Move statt Kopie
    
    	return database; // Edit¹: Nein, natürlich nicht immer ok. 
    	// In database liegen einfach alle Werte, die extrahiert werden konnten, der User muss auf eventuelle Fehler prüfen.
    }
    

    (ungetestet)
    Die komplette Fehlerbehandlung tritt dann in der aufrufenden Funktion auf, die auf EOF testen kann, die Datei öffnet, usw.

    Edit: SeppJ ist offenbar anderer Meinung. :p


  • Mod

    Ich korrigiere: Die for-Schleife in der main() ist natürlich ein Meisterwerk von schlecht lesbarem Code. Werner macht solche Schleifen dauernd. 👎 Aber sonst ist das doch eigentlich ganz gewöhnlicher Code.



  • Erstma danke für die vielen Antworten^^ 🙂 aber leider kann ich mit einigen sachen im Moment noch nix anfangen 😃 Ich denk ich werd einfach in 1-2 wochen nochmal hierdrauf zurückgreifen 🙂



  • Hallo Ruvi,

    Ruvi schrieb:

    Ich habe bis jetzt nur c++ code von mir gesehen und der ist bei weitem nicht so komplex.
    Ich bin ehrlich gesagt auch ein wenig geschockt.
    Ich habe jetzt an die 30-40min gebraucht um die 90 Zeilen Code von Werner im Detail zu verstehen und nachzuvollziehen.

    Aber sieht so, die Komplexitaet von c++ code (Schreibstil)in der Realitaet aus und ihr braucht mit den Augen nur einmal rueberfliegen und koennt alles verstehen?

    bedenke bitte, ich das seit Jahren (Jahrzehnten) beruflich mache, und Du - so vermute ich - noch relativ am Anfang stehst. Es erwartet auch niemand von einem Tischler-Lehrling im ersten oder zweiten Lehrjahr, dass er eine geschwungene Holztreppe inklusive Geländer entwerfen und bauen kann.

    Von daher brauchst Du nicht geschockt zu sein, das ist ganz normal. Und es freut mich, dass Du den Code immerhin verstanden hast. Das Muster ist immer das gleiche. Struktur anlegen, um eine Datenelement unterzubringen (hier Entry , bzw. std::pair<string,Entry> ), Extraktor dazu schreiben ( operator>> ), passenden Container wählen (hier std::map ) und dann Lesen-Prüfen-und-rein in den Container. Etwas Rahmenwerk drumherum und ggf. ein Helferlein (hier get_content ), um Redundanzen abzufangen und - ja und, um den Code lesbarer zu gestalten. Letzteres setzt natürlich voraus, dass man die Mechanismen dahinter kennt.

    @SeppJ, @Arcoth: Ihr habt natürlich recht - ich kann nicht mehr anders 😉 Ich gebe ja zu, die Zeile 71 ist Geschmacks- und Gewohnheitssache.
    Nehmen wir mal die Zeilen 51-53

    string account;
        for( Entry e;  in >> get_content( ACCOUNT, account ) >> e; )
            database[account] = e;
    

    es würde mir echt schwer fallen, satt dessen z.B. so etwas hin zuschreiben:

    for(;;) {
            string account;
            Entry e;
            in >> get_content( ACCOUNT, account );
            in >> e;
            if( !in.fail() )
                break;
            database[account] = e;
        }
    

    ich würde so was hier nicht posten.

    SeppJ schrieb:

    Werner ist einer der wenigen hier im Forum, die sich richtig gut mit den IOStreams auskennen ..

    Auf der einen Seite finde ich das ja nett, dass ich hier als Experte gehandelt werde, aber auf der anderen Seite finde ich gar nicht gut, dass sich nicht viel mehr Leute mit Streams auskennen - und das ist nicht nur im Forum so.
    Früher habe ich mal gelernt: Ein Programm ist Eingabe-Verarbeitung-Ausgabe - d.h. Ein- und Ausgabe sind also rudimentär und in jedem Programm vorhanden und das funktioniert in C++ eben mit Stream und Streambuf. Ich fänd's gut wenn mehr Leute damit arbeiten - schon wegen einer gewissen Standardisierung.

    Gruß
    Werner


Anmelden zum Antworten