fstream: Vokabeltrainer: Einlesung von text.txt funktioniert nur teilweise.
-
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.
-
Die Streuung der Hashfunktion zur zufälligen Reihenfolge der Vokabeln zu verwenden finde ich ziemlich panne.
Vor allem so wie du es machst, du nutzt die Map ja nichtmal zum Lookup.std::vector nehmen, die Pairs rein und std::random_shuffle. Fertig.
-
"Panne" ist, wenn man Wörterbuch-Aufgabenstellungen nicht mit Dictionaries löst und Anfängern, die wie hier Programmieren durch Ausprobieren praktizieren, mit allgemeinen Weisheiten über Vectoren kommt.
Ich habe mal gehört, dass sich Daten zur Laufzeit eines Programms auch ändern können, und was machst du dann?
Nach jedem Einfügen/Löschen nochmal Shuffle und den Gesamtdatenbestand anfassen?
Und nichtindiziertes Löschen aus Vectoren praktizierst du wohl auch sehr gern.
Natürlich kann man mit Vectoren auch ziemlich Ähnliches wie mit Dictionaries erreichen, man muss es aber nicht; und insbesondere nicht für Anfänger.
Mit solchen "Qualitäts"vorstellungen bei C++ Datenstrukturen für Anfänger kannst du den üblichen Tutorialschreiberlingen/Fachbuchautoren/Hochschuldozenten/push_back-Fetischisten/... die Hand reichen.
-
Wutz schrieb:
Nach jedem Einfügen/Löschen nochmal Shuffle und den Gesamtdatenbestand anfassen?
Die unordered_map verändert die Reihenfolge der ursprünglichen Elemente dann auch nicht (immer), also müsste push_back reichen. Wenn ein mischen gewünscht ist, geht das mit (genau) einer unordered_map gar nicht.
Wutz schrieb:
Und nichtindiziertes Löschen aus Vectoren praktizierst du wohl auch sehr gern.
swap(vec[i], vec.back()); vec.push_back();
Wutz schrieb:
Natürlich kann man mit Vectoren auch ziemlich Ähnliches wie mit Dictionaries erreichen, man muss es aber nicht; und insbesondere nicht für Anfänger.
Die Dinger sind grundverschieden. Vector hat eine feste Reihenfolge, unordered_map ist halb-zufällig und kaum beeinflussbar.
Mit solchen "Qualitäts"vorstellungen bei C++ Datenstrukturen für Anfänger kannst du den üblichen Tutorialschreiberlingen/Fachbuchautoren/Hochschuldozenten/push_back-Fetischisten/... die Hand reichen.
dito
-
pfann schrieb:
swap(vec[i], vec.back()); vec.push_back();
Du meinst pop_back
@Wutz: Da du immer von Dictionaries redest, zeugt dass eher davon, dass du von einer Sprache wie C# oder Java kommst.
Aber für das bisschen, was der TE hier erwartet, da reicht sowohl ein vector<pair<string, string> als auch eine map<string, string> als auch eine unordered_map<string, string>.
Wie wir alle wissen hat ejde dieser Strukturen seinen Vor- oder Nachteil. Aber gerade weil eben so wenig Rahmenbedingungen bekannt sind (oder auch sogar nur vorhanden/verlangt), reicht das doch.
-
#include <iostream> #include <vector> #include <string> #include <fstream> //#include <random> //c++ 11 #include <cstdlib> #include <ctime> #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(); int v_rand; srand(time(NULL)); string mem; if(language == "de"){ for(;;){ v_rand = rand() % (vocab.size())+0; cout << "Übersetze das Wort. STRG+D = Abbruch! Ausgangssprache [ " << language << " ] : " << vocab.at(v_rand).at(DEU) << endl; if(!(cin >> mem)) { break; } //Abbruch mit STRG+D(unter linux) if(mem == vocab.at(v_rand).at(ENG)){ cout << "Richtig!" << endl; ++right_w; } else{ cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << vocab.at(v_rand).at(ENG) << endl; ++false_w; } } } else if(language == "en"){ for(;;){ v_rand = rand() % (vocab.size())+0; cout << "Übersetze das Wort. STRG+D = Abbruch! Ausgangssprache [ " << language << " ] : " << vocab.at(v_rand).at(ENG) << endl; if(!(cin >> mem)) { break; } //Abbruch mit STRG+D(unter linux) if(mem == vocab.at(v_rand).at(DEU)){ cout << "Richtig!" << endl; ++right_w; } else{ cout << "Falsch!\t" << "Die richtige Vokabel lautet: " << vocab.at(v_rand).at(DEU) << endl; ++false_w; } } } cout << "Ergebnis:\trichtig: " << right_w << "\tfalsch: " << false_w << endl; return 0; } int main(int argc, char *argv[]) { string s1; if(argc > 1){ s1 = argv[1]; } if(s1.empty()){ 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; }
Problem gelöst. Programm läuft. Das ist für mich die beste Lösung. std::map und std::unordered_map habe ich natürlich auch ausprobiert. Da hatte ich aber später Probleme mit. Mit <random> habe ich mich kurz beschäftigt. Ich fand aber, dass rand() die bessere und einfachere Lösung für mein Programm war.
Sooo ich mache jetzt lieber in meinem Buch weiter, damit ich das nächste mal wenn ich wieder ein Problem haben sollte, nicht mit so einem Halbwissen wiederkomme... Danke an alle die mir helfen konnten.