fstream: Vokabeltrainer: Einlesung von text.txt funktioniert nur teilweise.



  • Guten Morgen,

    seit gestern möchte ich einen einfachen Vokabeltrainer programmieren. Dieser soll eine text.txt Datei einlesen. In dieser befinden sich die Deutschen und die dazugehörigen Englischen Vokabeln. Der Aufbau der text.txt sieht folgendermaßen aus:

    hallo	hello
    Messer	knife
    Bildschirm	screen
    Stuhl	chair
    

    An erster stelle steht eine Deutsche Vokabel. hinter dieser kommt ein

    '\t'
    

    .Darauf folgt die Englishe Vokabel und dahinter ein

    '\n'
    

    .

    meine main.cpp :

    #include <iostream>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <limits>
    using namespace std;
    
    void trainer(const string language){
    
        string mem;
        int right_w(0), false_w(0);
    
        ifstream in;
        in.open("text.txt");
        if (!in.is_open()) { cerr << "FEHLER: Datei konnte nicht geöffnet werden!" << endl;}
    
        string smem; //Für einzelne Vokabeln
        string s_all; //In diesen String wird die Datei "text.txt" komplett kopiert.
        vector<string> en_svec; //Vector für alle englishen Vokabeln.
        vector<string> de_svec; //Vector für alle deutschen Vokabeln.
    
        char character;
        char curr_language = 'd'; //Standartmäßig auf Deutsch
        while ((character = in.get()) != -1) {
            s_all += character;
        }
        for(size_t ix =0; ix != s_all.size(); ++ix){
            if(curr_language == 'd'){
    
                if(s_all[ix] == '\t' || s_all[ix] == '\n'){
                    if(s_all[ix] == '\t'){
                        curr_language = 'e';
                        continue;
                    }
                }
                else{
                    smem += s_all[ix];
                    if(s_all[ix+1] == '\t'){
                        de_svec.push_back(smem);
                        smem = "";
                    }
                }
    
            }
    
            else if(curr_language == 'e'){
    
                if(s_all[ix] == '\t' || s_all[ix] == '\n'){
                    if(s_all[ix] == '\n'){
                        curr_language = 'd';
                        continue;
                    }
                }
                else{
                    smem += s_all[ix];
                    if(s_all[ix+1] == '\n'){
                        en_svec.push_back(smem);
                        smem = "";
                    }
                }
    
            }
        }
    
        if(language == "de"){
            if(sizeof(de_svec) == sizeof(en_svec)){
                for(size_t ix=0; ix != (sizeof(de_svec)/sizeof(string)); ++ix){
                    cout << "Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ " << language << " ] : "
                         << de_svec[ix] << endl;
                    cin >>  mem;
                    if(mem == en_svec[ix]){
                        cout << "Richtig!" << endl;
                        ++right_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                    else{
                        cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << en_svec[ix] << endl;
                        ++false_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                }
            }
            cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl;
        }
        else if(language == "en"){
            if(sizeof(en_svec) == sizeof(de_svec)){
                for(size_t ix=0; ix != (sizeof(en_svec)/sizeof(string)); ++ix){
                    cout << "Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ " << language << " ] : "
                         << en_svec[ix] << endl;
                    cin >>  mem;
                    if(mem == de_svec[ix]){
                        cout << "Richtig!" << endl;
                        ++right_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                    else{
                        cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << de_svec[ix] << endl;
                        ++false_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                }
            }
            cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl;
        }
    }
    
    int main()
    {
    
        string lang;
        cout << "Wähle die Sprache. [en] für English->Deutsch. [de] für Deutsch->Englisch" << endl;
        while(cin >> lang){
            if(lang == "de")
                break;
            else if(lang == "en")
                break;
            else{
                cerr << "FEHLER: Falsche Eingabe. Eingabe muss [en] oder [de] lauten!" << endl;
                continue;
            }
        }
        trainer(lang);
        return 0;
    }
    

    Die Ausführung des Programms:

    krex@krex-ubuntu:~/Dokumente/cpp/Vokabeltrainer/Forum$ ./Vokabeltrainer 
    Wähle die Sprache. [en] für English->Deutsch. [de] für Deutsch->Englisch
    en
    Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ en ] : hello
    hallo
    Richtig!
    Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ en ] : knife
    Messssser
    Falsch!	Die richtige Vokabel lautet: Messer
    Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ en ] : screen
    Bildschirm
    Richtig!
    Ergebnis:	richtig: 2	falsch: 1
    krex@krex-ubuntu:~/Dokumente/cpp/Vokabeltrainer/Forum$
    

    Das Programm heißt hier

    Vokabeltrainer
    

    weil mein Projekt so heißt.
    Die .txt Datei enthält insgesamt 46 Vokabeln. 23 Deutsche und 23 Englishe.

    hallo	hello
    Messer	knife
    Bildschirm	screen
    Stuhl	chair
    Boden	floor
    Tasche	bag
    Flasche	bootle
    Auto	car
    Lampe	lamp
    Licht	light
    Person	person
    Spieler	player
    Maus	mouse
    Käse	cheese
    Schinken	bacon
    Holz	wood
    Baum	tree
    Bett	bed
    Nacht	night
    schlafen	sleep
    Eisen	iron
    Telefon	telephone
    Batterie	battery
    

    Wie man an der Ausführung des Programms erkennen kann, fragt mich das Programm nur 3 mal ab, obwohl in der text.txt Datei 46 Vokabeln stehen. Wenn ich in Zeile 68 diesen Codeabschnitt einfüge :

    ////////////////////////////////////////////////////////////////////////////////////
        //Ausgabe von en_svec. Nur zum testen ob en_svec alle Elemente enthält!
        for(vector<string>::iterator iter = en_svec.begin(); iter != en_svec.end(); ++iter){
            cout << "Ausgabe en_svec in int main() :" << *iter << endl;
        }
        ////////////////////////////////////////////////////////////////////////////////////
    
        ////////////////////////////////////////////////////////////////////////////////////
        //Ausgabe von de_svec. Nur zum testen ob de_svec alle Elemente enthählt!
        for(vector<string>::iterator iter = de_svec.begin(); iter != de_svec.end(); ++iter){
            cout << "Ausgabe de_svec in int main() :" << *iter << endl;
        }
        ////////////////////////////////////////////////////////////////////////////////////
    

    um zu überprüfen ob alle Elemente in

    de_svec
    

    und

    en_svec
    

    sind, werden alle Vokabeln die auch in der text.txt Datei stehen, aufgelistet.
    Der Fehler muss also irgendwo ab Zeile 68 aufwärts liegen. Das Programm bin ich mehrmals im Kopf durchgegangen, mir fällt der Fehler aber nicht auf. Ich bitte um Eure Hilfe.



  • Was du da machst ist an so vielen Stellen schlecht oder sogar falsch.

    if(sizeof(de_svec) == sizeof(en_svec))
    

    Macht auf jedenfall nicht das was du denkst. (Ich glaube, bin mir aber nicht sicher, dass da immer true rauskommt.)

    Du meinst dabei eher das hier:

    if ( de_svec.siz() == en_svec.size() )
    

    Dann:
    Du liest die Datei kompliziert in mehreren Schleifen ein, verschachtelst noch ein paar ifs da rein und so.
    Wieso liest du nicht je eine Vokabel in je einen Sprachcontainer ein? Da du ja weisst wie die Datei aussieht. Im simpelsten Fall sieht das so aus:

    vector<string> deu;
    vector<string> eng;
    
    ifstream ifs("text.txt");
    
    while ( ifs )
    {
        string tmpD, tmpE;
        ifs >> tmpD >> tmpE;
        if ( ifs )
        {
            deu.push_back(tmpD);
            deu.push_back(tmpE);
        }
    }
    

    Und selbst das ist verbesserbar.

    Und da du ja schon std::vector kennst, schau dir mal std::map an 😉



  • Lass dir mal die Werte, die bei deinen

    sizeof(..)
    

    raus kommen, ausgeben.

    EDIT: Zu langsam. 😞



  • cout << sizeof(en_svec) << endl;
    

    Ergibt:

    24
    
    cout << sizeof(en_svec) << endl;
    

    Ergibt:

    24
    

    Skym0sh0 schrieb:

    Was du da machst ist an so vielen Stellen schlecht oder sogar falsch.

    if(sizeof(de_svec) == sizeof(en_svec))
    

    Macht auf jedenfall nicht das was du denkst. (Ich glaube, bin mir aber nicht sicher, dass da immer true raus kommt.)

    ups. ja hast recht.
    habs jetzt in

    if((sizeof(en_svec)/sizeof(string)) == (sizeof(de_svec)/sizeof(string))){
    

    geändert. Das sollte einen booleschen Wert zurückgeben der 1 lautet wenn en_svec genauso viel Elemente hat wie de_svec.

    Wenn ich

    (sizeof(en_svec)/sizeof(string))
    

    lautet die Ausgabe

    3
    

    Da ist der Fehler!
    Darum werde ich nur 3 mal abgefragt.
    Die Ausgabe muss 23 heißen.
    Warum heißt die Ausgabe denn 3?



  • Problem gelöst!
    Wenn auch sehr umständlich. Ich werde mich bemühen den Code noch zu verbessern.

    #include <iostream>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <limits>
    using namespace std;
    
    void trainer(const string language){
    
        string mem;
        int right_w(0), false_w(0);
    
        ifstream in;
        in.open("text.txt");
        if (!in.is_open()) { cerr << "FEHLER: Datei konnte nicht geöffnet werden!" << endl;}
    
        string smem; //Für einzelne Vokabeln
        string s_all; //In diesen String wird die Datei "text.txt" komplett kopiert.
        vector<string> en_svec; //Vector für alle englishen Vokabeln.
        vector<string> de_svec; //Vector für alle deutschen Vokabeln.
    
        char character;
        char curr_language = 'd'; //Standartmäßig auf Deutsch
        while ((character = in.get()) != -1) {
            s_all += character;
        }
        for(size_t ix =0; ix != s_all.size(); ++ix){
            if(curr_language == 'd'){
    
                if(s_all[ix] == '\t' || s_all[ix] == '\n'){
                    if(s_all[ix] == '\t'){
                        curr_language = 'e';
                        continue;
                    }
                }
                else{
                    smem += s_all[ix];
                    if(s_all[ix+1] == '\t'){
                        de_svec.push_back(smem);
                        smem = "";
                    }
                }
    
            }
    
            else if(curr_language == 'e'){
    
                if(s_all[ix] == '\t' || s_all[ix] == '\n'){
                    if(s_all[ix] == '\n'){
                        curr_language = 'd';
                        continue;
                    }
                }
                else{
                    smem += s_all[ix];
                    if(s_all[ix+1] == '\n'){
                        en_svec.push_back(smem);
                        smem = "";
                    }
                }
    
            }
        }
        /*
        ////////////////////////////////////////////////////////////////////////////////////
        //Ausgabe von en_svec. Nur zum testen ob en_svec alle Elemente enthält!
        for(vector<string>::iterator iter = en_svec.begin(); iter != en_svec.end(); ++iter){
            cout << "Ausgabe en_svec in int main() :" << *iter << endl;
        }
        ////////////////////////////////////////////////////////////////////////////////////
    
        ////////////////////////////////////////////////////////////////////////////////////
        //Ausgabe von de_svec. Nur zum testen ob de_svec alle Elemente enthählt!
        for(vector<string>::iterator iter = de_svec.begin(); iter != de_svec.end(); ++iter){
            cout << "Ausgabe de_svec in int main() :" << *iter << endl;
        }
        ////////////////////////////////////////////////////////////////////////////////////
        */
    
        if(language == "de"){
            if((sizeof(en_svec)/sizeof(string)) == (sizeof(de_svec)/sizeof(string))){
                for(vector<string>::iterator de_iter = de_svec.begin(), en_iter = en_svec.begin(); de_iter != de_svec.end() && en_iter != en_svec.end(); ++de_iter, ++en_iter){ //backup:for(size_t ix=0; ix != (sizeof(de_svec)/sizeof(string)); ++ix){
                    cout << "Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ " << language << " ] : "
                         << *de_iter << endl;
                    cin >>  mem;
                    if(mem == *en_iter){
                        cout << "Richtig!" << endl;
                        ++right_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                    else{
                        cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << *en_iter << endl;
                        ++false_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                }
            }
            cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl;
        }
        else if(language == "en"){
            if((sizeof(en_svec)/sizeof(string)) == (sizeof(de_svec)/sizeof(string))){
                for(vector<string>::iterator de_iter = de_svec.begin(), en_iter = en_svec.begin(); de_iter != de_svec.end() && en_iter != en_svec.end(); ++de_iter, ++en_iter){ //backup:for(size_t ix=0; ix != (sizeof(de_svec)/sizeof(string)); ++ix){
                    cout << "Übersetze das Wort hinter dem Doppelpunkt. Ausgangssprache [ " << language << " ] : "
                         << *en_iter << endl;
                    cin >>  mem;
                    if(mem == *de_iter){
                        cout << "Richtig!" << endl;
                        ++right_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                    else{
                        cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << *de_iter << endl;
                        ++false_w;
                        cin.clear();
                        cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
                    }
                }
            }
            cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl;
        }
    
        /*
        cout << (sizeof(en_svec)/sizeof(string)) << endl; //liefert 3
        cout << (sizeof(de_svec)/sizeof(string)) << endl; //liefert 3
        */
    }
    
    int main()
    {
    
        string lang;
        cout << "Wähle die Sprache. [en] für English->Deutsch. [de] für Deutsch->Englisch" << endl;
        while(cin >> lang){
            if(lang == "de")
                break;
            else if(lang == "en")
                break;
            else{
                cerr << "FEHLER: Falsche Eingabe. Eingabe muss [en] oder [de] lauten!" << endl;
                continue;
            }
        }
        trainer(lang);
        return 0;
    }
    

    Edit: Hat jemand Verbesserungsvorschläge? Es gibt bestimmt viele...



  • Ich würde sagen die Funktion trainer ist etwas groß und erfüllt zu viele Aufgaben.

    Du könntest ganz leicht einbauen, dass die Textdatei nicht "gehardcoded" ist, sondern dass der Benutzer selbst bestimmt, welche Vokabeldatei er verwenden möchte. Dann kannst du immer für bestimmte Vokabeln bestimmte Dateien verwenden.
    Beachte hierbei, dass du auf den string mit dem Dateinahmen .c_str() anwendest, damit der istream damit was anfangen kann.

    Lies direkt aus der Datei deine Infos ein, nicht davor noch in einen riesen String kopieren oder sonst was machen. Das wurde aber glaube auch schon gesagt 😉

    if((sizeof(en_svec)/sizeof(string)) == (sizeof(de_svec)/sizeof(string)))

    if( en_svec.size() == de_svec.size())

    Einfach mal die Vorschläge der Vorposter nehmen, Map anstatt 2 Vektoren, ...

    mfg
    HarteWare



  • Keksman schrieb:

    ups. ja hast recht.
    habs jetzt in

    if((sizeof(en_svec)/sizeof(string)) == (sizeof(de_svec)/sizeof(string))){
    

    geändert.

    Hast du den Post von Skym0sh0 eigentlich gelesen?



  • Cyres schrieb:

    Keksman schrieb:

    ups. ja hast recht.
    habs jetzt in

    if((sizeof(en_svec)/sizeof(string)) == (sizeof(de_svec)/sizeof(string))){
    

    geändert.

    Hast du den Post von Skym0sh0 eigentlich gelesen?

    Ja habe ich.
    Allerdings hat mir Skym0sh0 vorgeschlagen

    if ( de_svec.siz() == en_svec.size() )
    

    zu benutzen. Ich wusste nicht was die Funktion siz() macht, also habe ich das vorerst ignoriert... Erst danach wurde mir klar, dass dies ein Tippfehler war. Ist mir erst relativ spät aufgefallen. Naja egal.
    Hier mal mein aktueller Code:

    #include <iostream>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <limits>
    #include <stdexcept>
    using namespace std;
    
    int trainer(const string language, const string s1){
        string mem;
        int right_w(0), false_w(0);
    
        ifstream in(s1.c_str());
        if (!in.is_open()) { throw runtime_error("FEHLER: Datei konnte nicht geöffnet werden! Eventuell existiert die Datei nicht."); }
    
        string smem;
        vector<string> en_svec; //Vector für alle englishen Vokabeln.
        vector<string> de_svec; //Vector für alle deutschen Vokabeln.
    
        for(char character, curr_language = 'd'; (character = in.get()) != -1;){
            if(curr_language == 'd'){
                if(character == '\t'){
                        de_svec.push_back(smem);
                        smem = ""; //string reset
                        curr_language = 'e';
                }
                else{ smem += character; }
            }
            else if(curr_language == 'e'){
                if(character == '\n'){
                        en_svec.push_back(smem);
                        smem = ""; //string reset
                        curr_language = 'd';
                }
                else { smem += character; }
            }
        }
        in.close();
    
        if(language == "de"){        
            for(vector<string>::iterator de_iter = de_svec.begin(), en_iter = en_svec.begin(); de_iter != de_svec.end() && en_iter != en_svec.end(); ++de_iter, ++en_iter){
                cout << "Übersetze das Wort. STRG+D = Abbruch! Ausgangssprache [ " << language << " ] : "
                     << *de_iter << endl;
                if(!(cin >> mem)) { break; } //Abbruch mit STRG+D lautet(unter linux)
                if(mem == *en_iter){
                    cout << "Richtig!" << endl;
                    ++right_w;
                }
                else{
                    cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << *en_iter << endl;
                    ++false_w;
                }
            }
        }
        else if(language == "en"){
            for(vector<string>::iterator de_iter = de_svec.begin(), en_iter = en_svec.begin(); de_iter != de_svec.end() && en_iter != en_svec.end(); ++de_iter, ++en_iter){
                cout << "Übersetze das Wort. STRG+D = Abbruch! Ausgangssprache [ " << language << " ] : "
                     << *en_iter << endl;
                if(!(cin >> mem)) { break; } //Abbruch mit STRG+D(unter linux)
                if(mem == *de_iter){
                    cout << "Richtig!" << endl;
                    ++right_w;
                }
                else{
                    cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << *de_iter << endl;
                    ++false_w;
                }
            }
        }
        cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl;
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        cout << "Für Hilfe: ./Vokabeltrainer --help" << "\tProgramm abbrechen = STRG+C" << endl;
        string s1;
        if(argc > 1){
            s1 = argv[1];
            if(s1 == "--help"){
                cout << "Benutze: ./Vokabeltrainer <VOKABELQUELLE.txt>" << endl;
            }
        }
    
        string lang;
        cout << "Wähle die Sprache. [en] für English->Deutsch. [de] für Deutsch->Englisch" << endl;
        while(cin >> lang){
            if(lang == "de")
                break;
            else if(lang == "en")
                break;
            else{
                cerr << "FEHLER: Falsche Eingabe. Eingabe muss [en] oder [de] lauten!" << endl;
                continue;
            }
        }
        trainer(lang, s1);
        return 2;
    }
    

    Das Programm soll die Vokabeln außerdem noch zufällig aufrufen. Der Benutzer soll diese dann übersetzen. Mit srand() und rand() habe ich das schon versucht. Allerdings klappt dies nicht so wie ich es mir vorstelle. Wenn ich rand() schnell hintereinander aufrufe bekomme ich immer die gleiche Zahl. wenn ich ca 1 Sekunde warte, bekomme ich vielleicht, aber nicht immer eine andere Zahl.

    Die Abfrage ob de_svec genauso viele Elemente hat wie en_svec habe ich entfernt weil dies immer true ergeben würde. Hier die Erklärung:
    Das Ende meiner .txt sieht in meinem Texteditor "gedit" folgendermaßen aus:('\t' für Tabulator und '\n' für Zeilenumbruch)
    [/code]Buch\tbook\nTisch\t

    Ich dachte die ganze Zeit, dass das lette Zeichen ein '\t'. In wirklichkeit ist es aber ein '\n'. Warum hängt der dieses Zeichen an das Ende meiner .txt Datei an?
    [code="cpp"]for(char character, curr_language = 'd'; (character = in.get()) != -1;){
            if(curr_language == 'd'){
                if(character == '\t'){
                        de_svec.push_back(smem);
                        smem = ""; //string reset
                        curr_language = 'e';
                }
                else{ smem += character; }
            }
            else if(curr_language == 'e'){
                if(character == '\n'){
                        en_svec.push_back(smem);
                        smem = ""; //string reset
                        curr_language = 'd';
                }
                else { smem += character; }
            }
        }
    

    Nachdem das Programm den string "Buch" an den vector de_svec gehängt hat, setzt es curr_language auf 'e' weil das darauf folgende Wort normalerweise ein Englisches ist. Dies ist aber nicht der Fall. Das nachfolgende Zeichen in dem Fall das letzte von der *.txt Datei handelt es sich ja um ein '\n' Zeichen. Also wird der leere string smem an den vector en_svec gehängt. de_svec ist somit genau so groß wie en_svec.



  • srand muss am Anfang des Programms einmal aufgerufen werden, danach nie wieder (es sei denn du weisst was du machst und willst es auch so machen).

    rand kannst du danach beliebig aufrufen.

    Aber im neuen C++11 gibt es Zufallsgeneratoren.

    Aber mal ohne Spass, du machst mit Iteratoren und sowas allem rum, kensnt aber vector.size() nicht?



  • Aber mal ohne Spass, du machst mit Iteratoren und sowas allem rum, kensnt aber vector.size() nicht?

    Doch Doch Doch. Ich kenne den.

    Allerdings hat mir Skym0sh0 vorgeschlagen
    C++:
    if ( de_svec.siz() == en_svec.size() )

    zu benutzen. Ich wusste nicht was die Funktion siz() macht, also habe ich das vorerst ignoriert... Erst danach wurde mir klar, dass dies ein Tippfehler war. Ist mir erst relativ spät aufgefallen.

    Ich weiß nicht warum ich vector.size() nicht verwendet habe. Ich hatte da sowas mit sizeof noch im Kopf.

    if ( de_svec.siz() == en_svec.size() )
    

    Wenn du

    if ( de_svec.size() == en_svec.size() )
    

    geschrieben hättest, wäre ich drauf gekommen. ich hab nur nicht drüber nachgedacht, dass du mit siz() , size() meinst!

    for(char character, curr_language = 'd'; (character = in.get()) != -1;){
            if(curr_language == 'd'){
                if(character == '\t' || character == '\n'){
                    if(smem == "") { continue; }
                    else{
                        de_svec.push_back(smem);
                        smem = ""; //string reset
                        curr_language = 'e';
                    }
                }
                else{ smem += character; }
            }
            else if(curr_language == 'e'){
                if(character == '\n' || character == '\t'){
                    if(smem == "") { continue; } //Wenn smem leer ist, wird die Schleife weiter ausgeführt.
                    else{
                        en_svec.push_back(smem);
                        smem = ""; //string reset
                        curr_language = 'd';
                    }
                }
                else { smem += character; }
            }
        }
        in.close();
        if(de_svec.size() != en_svec.size()) { throw runtime_error("FEHLER: *.txt Datei ist beschädigt!"); }
    

    So funktioniert es.
    Edit: Bitte nennt mir Dinge die an meinem Code nicht so gut sind. Ich weiß gerade überhaupt nicht wie ich den Zufallsgenerator in den Code einbauen könnte. Würde mir jemand einen Tipp geben?



  • Und jetzt guckste du dir mal std::map an und machst dein programm gleich mal um 75% kleiner und schneller.



  • Skym0sh0 schrieb:

    Und jetzt guckste du dir mal std::map an und machst dein programm gleich mal um 75% kleiner und schneller.

    Map ist hier völlig fehl am Platz. Das ist ein Fall für vector<pair<string,string> >



  • Mh, hört sich auch nicht schlecht an. Ja, ist echt gar nichtmal so schlecht.



  • Nimm boost::spirit.



  • Also,

    ich habe mir std::map mal angeguckt http://www.cplusplus.com/reference/map/map/
    und ein Unterverzeichnis http://www.cplusplus.com/reference/map/map/map/
    Ist das hier ein Anfang?

    vector < map<string,string> > de_en;
    

    Wenn ja, wie hänge ich Elemente dran?

    foo.push_back(("Hi")=("there!"));
    

    So ist es schonmal falsch. War ja auch geraten...

    Map ist hier völlig fehl am Platz. Das ist ein Fall für vector<pair<string,string> >

    ist da ein unterschied zu std::map?
    Ich bin gerade ein "bisschen" überfordert. Aus der Referenz werde ich einfach nicht schlau 😞



  • map ist ein assoziativer Container. Das heisst, du ordnest einem Key einen Value zu. Dabei können Key und Value (fast) beliebige Datentypen sein.
    Das kennst du im Prinzip schon vom Vector. Da hast du einem index (normalerweise ein vorzeichenloser Integer) einen Wert zugeordnet. Nichts anderes macht eine map.

    int main()
    {
    	std::map<std::string, double> m;
    	double bar = 1.618;
    
    	m["Test"] = 5.0f;
    	m["pi"] = 3.14159276;
    	m["foo"] = bar;
    
    	for(std::map<std::string, double>::iterator it = m.begin(); it != m.end(); ++it)
    	{
    		// it <-- std::pair<std::string, double>
    		std::cout << "m[" << it->first << "] = " << it->second << std::endl;
    	}
    
    	return 0;
    }
    

    Um mal einfachen Beispielcode zu zeigen. Iteratoren bieten ganz gerne ein bisschen was zum Stolpern, aber da kommt man drüber.

    Was unmap aber meint ist, dass diese map hier fehl am Platz ist.
    Er schlägt einen vector vor, der Paare von strings speichert. Das sieht dann so aus:

    int main()
    {
    	std::vector<std::pair<std::string, std::string>> vec;
    
    	// vector fuellen
    	vec.push_back(std::make_pair("tisch", "table"));
    	vec.push_back(std::make_pair("verdorbenheit", "depravity"));
    	vec.push_back(std::make_pair("belastung", "imposition"));
    	// ....
    
    	// hier kannst du dann deine vokabeln raten:
    	std::default_random_engine gen (seed);
    	std::uniform_int_distribution<int> rand(1, vec.size());
    
    	std::pair<std::string, std::string> question = vec[rand(gen)];
    
    	std::cout << "Was heißt \"" << question.first << "\": ";
    
    	std::string answer;
    	std::getline(std::cin, answer);
    
    	if ( answer == question.second )
    		std::cout << "Richtig!" << std::endl;
    	else
    		std::cout << "Falsch!" << std::endl;
    
    	return 0;
    }
    

    (Nur ausem Kopf geschrieben, grad beim Zufallsgenerator bin ich nicht sicher, da ich den bisher kaum geschrieben hab ;))



  • Danke für deine gute und schnelle Erklärung Skym0sh0 🙂
    Ich versuche mal std::map in meinen Code zu implementieren. Wenn mir das gelingt, versuche ich noch den vector zu implementieren, der wie du sagst Paare von strings speichert.
    Sooo ... Ich bin müde und gehe schlafen. Code kommt morgen.
    Gute Nacht.



  • Skym0sh0 schrieb:

    std::vector<std::pair<std::string, std::string>> vec;
    	
    	...
    	std::default_random_engine gen (seed);
    

    Grausam umständlich.
    unordered_map kann das alles schon, einfach iterieren für eine Trainingsfunktion und für einen normalen Übersetzungslauf dann [] (mit count() zur Absicherung bei Nichtexistenz);
    das alles leistet ein und dieselbe Datenstruktur, d.h. du brauchst für beide gängige Anwendungsfälle nicht immer eine neue aufzubauen und die ganzen Daten hin- und herkopieren.
    Alles viel kürzer und somit weniger fehleranfällig.



  • Mein aktueller Code:

    #include <iostream>
    #include <vector>
    #include <string>
    #include <fstream>
    #include <limits>
    #include <stdexcept>
    
    #define ENG 0
    #define DEU 1
    
    using namespace std;
    
    int trainer(const string language, const string s1){
        unsigned int right_w(0), false_w(0);
    
        ifstream in;
        char c;
        string wordCache;
        vector < string > current;
        vector < vector < string > > vocab;
    
        in.open(s1.c_str()); // Open the file in read-only mode
        if (!in.is_open()) return 1; // Check whether it was opened successfully
    
        while ((c = in.get()) != -1) { // Walk through the file
            if (c == '\t' || c == '\n') { // Delimiter
                current.push_back(wordCache); // Add cached text to the array
                wordCache = ""; // Reset cache
            }
            else {
                wordCache += c; // Otherwise just append the current character to our word cache
            }
            if (c == '\n') { // In case we hit a newline,
                vocab.push_back(current); // Add the cached vocab pair to the vocab array
                current.clear(); // And don't forget to clear the vocab pair
            }
        }
    
        in.close();
        string mem;
        if(language == "de"){        
            for(unsigned int i=0; i < vocab.size(); ++i){
                cout << "Übersetze das Wort. STRG+D = Abbruch! Ausgangssprache [ " << language << " ] : "
                     << vocab.at(i).at(DEU) << endl;
                if(!(cin >> mem)) { break; } //Abbruch mit STRG+D(unter linux)
                if(mem == vocab.at(i).at(ENG)){
                    cout << "Richtig!" << endl;
                    ++right_w;
                }
                else{
                    cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << vocab.at(i).at(ENG) << endl;
                    ++false_w;
                }
            }
        }
        else if(language == "en"){
            for(unsigned int i=0; i < vocab.size(); ++i){
                cout << "Übersetze das Wort. STRG+D = Abbruch! Ausgangssprache [ " << language << " ] : "
                     << vocab.at(i).at(ENG) << endl;
                if(!(cin >> mem)) { break; } //Abbruch mit STRG+D(unter linux)
                if(mem == vocab.at(i).at(DEU)){
                    cout << "Richtig!" << endl;
                    ++right_w;
                }
                else{
                    cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << vocab.at(i).at(DEU) << endl;
                    ++false_w;
                }
            }
        }
        cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl;
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        cout << "Für Hilfe: ./Vokabeltrainer --help" << "\tProgramm abbrechen = STRG+C" << endl;
        string s1;
        if(argc > 1){
            s1 = argv[1];
            if(s1 == "--help"){
                cout << "Benutze: ./Vokabeltrainer <VOKABELQUELLE.txt>" << endl;
                return 3;
            }
        }
    
        string lang;
        cout << "Wähle die Sprache. [en] für English->Deutsch. [de] für Deutsch->Englisch" << endl;
        while(cin >> lang){
            if(lang == "de")
                break;
            else if(lang == "en")
                break;
            else{
                cerr << "FEHLER: Falsche Eingabe. Eingabe muss [en] oder [de] lauten!" << endl;
                continue;
            }
        }
        trainer(lang, s1);
        return 2;
    }
    

    Diesen Code Abschnitt:

    while ((c = in.get()) != -1) { // Walk through the file
            if (c == '\t' || c == '\n') { // Delimiter
                current.push_back(wordCache); // Add cached text to the array
                wordCache = ""; // Reset cache
            }
            else {
                wordCache += c; // Otherwise just append the current character to our word cache
            }
            if (c == '\n') { // In case we hit a newline,
                vocab.push_back(current); // Add the cached vocab pair to the vocab array
                current.clear(); // And don't forget to clear the vocab pair
            }
        }
    

    habe ich nicht selber geschrieben. Mein c++ Wissen reichte einfach nicht aus(ca. 1 guten Monat am proggen).
    Ich wusste gar nicht, dass man einen vector der 2 Elemente hat an einen anderen vector anhängen kann.

    vocab.push_back(current);
    

    Macht

    current.clear();
    

    noch etwas anderes als alle Elemente von

    std::vector<string> current;
    

    zu löschen?

    #define
    

    kannte ich vorher auch noch nicht. Laut Google ist das ein Präprozessorobject.
    Ich versuch mich mal an <random>



  • Keksman schrieb:

    habe ich nicht selber geschrieben. Mein c++ Wissen reichte einfach nicht aus

    Das der anderen auch nicht.
    Grausames char-Gefrickel zum simplen Einlesen von Strings aus einer Textdatei.

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <unordered_map>
    
    using namespace std;
    
    int trainer(unordered_map<string,string> &d)
    {
      int fehler=0;
      for(unordered_map<string,string>::iterator i=d.begin();i!=d.end();++i)
      {
        cout << i->first << " ? ";
        string s;
        cin >> s;
        if( i->second==s )
        {
          cout << "Richtig\n";
        }
        else
        {
          fehler++;
          cout << "Falsch\n";
        }
      }
      return fehler;
    }
    
    int main()
    {
      string lang,d,e;
      cout << "Wähle die Sprache. [en] für English->Deutsch. [de] für Deutsch->Englisch" << endl;
      cin >> lang;
    
      ifstream f("text.txt");
      unordered_map<string,string> de,en; // vernünftige Programmiersprachen bieten von Haus aus natürlich auch bidirektionale Dictionaries an, da entfällt dann diese häßliche Daten-Dopplung
    
      while( f>>d && f>>e )
        de[d]=e,en[e]=d;
    
      cout << "Fehlerzahl: "  << trainer(lang=="de"?de:en);
    }
    

    unordered_map hat hier gegenüber vector<pair> den Vorteil, dass die Reihenfolge der abgefragten Vokabeln (meist) nicht der Reihenfolge in der Datei entspricht, und gegenüber map nicht sortiert abgefragt wird, das random-Zeugs also entfällt.


Anmelden zum Antworten