Anfängerfragen zu einem Projekt mit Klassen
-
Hallo ventilatores, ich hab mal zum Spass dir eine Klasse geschrieben die Dir die Wörter aus deiner Datei auflistet, zählt und sortiert, so wie du es im letzten Post gerne gehabt hättest. Viel Spass

Dauer 3Std 45min.
Datei wc.hpp
#ifndef WC_HPP_INCLUDED #define WC_HPP_INCLUDED #include <string> #include <fstream> #include <vector> #include <map> struct Wort { std::string Name; int Anzahl; }; class CWortCount { private: std::vector<Wort> m_Worte; // enthält die Worte : Anzahl für die Ausgabe std::ifstream* m_pfs; // zeiger auf file handle void ZaehleWorte(std::ifstream* fs); // zerlegt die Datei in Worte void WortZurMap(std::string& strWort, std::map<std::string, int>& mapWorte); // fügt neue Worte ein, erhöht den Zähler void KopiereMapZuVektor(std::vector<Wort>& vecWorte, std::map<std::string,int>& mapWorte);// kopiert die Worte in einen vektor für den // besseren Zugriff public: void SortCount(bool asc = true); // sortieren nach Anzahl void SortWord(bool asc = true); // lexikalisch sortieren bool OeffneDatei(std::string& Datei); // Oeffnet die Datei zu lesen Wort& operator[](const long idx); // liefert alle Wörter und deren Anzahl long WortAnzahl() { return m_Worte.size(); } // Anzahl der gefunden Woerter }; #endif // WC_HPP_INCLUDEDDatei: wc.cpp
#include "wc.hpp" #include <iostream> #include <map> using namespace std; bool CWortCount::OeffneDatei(string& Datei) { m_pfs = new ifstream(Datei.c_str()); if (!m_pfs->is_open()) { delete m_pfs; return false; } ZaehleWorte(m_pfs); // aufräumen brauchen wir nicht mehr // close wird automaisch aufgerufen delete m_pfs; return true; } void CWortCount::ZaehleWorte(ifstream* fs) { // Zerlegen des input streams in worte char c; string wort; map<string, int> mapWorte; // enthält die gefundenen (Worte:Anzahl) while (fs->get(c)) { switch (c) { case ',': case '.': case ' ': case '\n': case ';': // worttrenner gefunden if (wort.length() > 0) { WortZurMap(wort, mapWorte); wort.clear(); } break; default: wort.push_back(c); // addiere Zeichen zum Wort; break; } } if (!fs->eof()) cout << "DBGM: Filestream nicht leer !!" << endl; KopiereMapZuVektor(m_Worte, mapWorte); } void CWortCount::WortZurMap(string& strWort, map<string, int>& mapWorte) { pair<map<string,int>::iterator,bool> cmp; // versuche das wort einzufügen cmp = mapWorte.insert (pair<string,int>(strWort,1) ); // wort ist schon vorhanden erhöhe den Zähler; if (cmp.second == false) (*cmp.first).second++; } Wort& CWortCount::operator[](const long idx) { return m_Worte.at(idx); } void CWortCount::KopiereMapZuVektor(vector<Wort>& vecWorte, map<string,int>& mapWorte ) { map<string,int>::iterator it; for (it = mapWorte.begin(); it != mapWorte.end(); it++) { Wort w = { (*it).first, (*it).second }; vecWorte.push_back(w); // erzeugt eine Kopie und wird owner } } void CWortCount::SortCount(bool asc) { // ganz billige sortierung! for(size_t i = 0; i < m_Worte.size()-1; i++) { for(size_t j = i+1 ; j < m_Worte.size(); j++) if ((m_Worte.at(i).Anzahl > m_Worte.at(j).Anzahl) == asc) { Wort temp = m_Worte.at(j); m_Worte.at(j) = m_Worte.at(i); m_Worte.at(i) = temp; // kleinstes element speichern } } } void CWortCount::SortWord(bool asc) { // ganz billige sortierung! for(size_t i = 0; i < m_Worte.size()-1; i++) { for(size_t j = i+1 ; j < m_Worte.size(); j++) if ((m_Worte.at(i).Name > m_Worte.at(j).Name) == asc) { Wort temp = m_Worte.at(j); m_Worte.at(j) = m_Worte.at(i); m_Worte.at(i) = temp; // kleinstes element speichern } } }Datei: main.cpp
#include <iostream> #include "wc.hpp" using namespace std; int main() { string datei = "meine.dat"; CWortCount wc; // Ich hatte keine Lust den operator! zu überladen um zu prüfen // ob der Vorgang erfolgreich war, daher ist die Initialsierung // im Konstruktor noch nicht möglich if (!wc.OeffneDatei(datei)) cout << "Kann die Datei nicht oeffnen : " << datei << endl; wc.SortWord(); cout << "Alle gefunden Wörter in: " << datei << endl; for(int i = 0; i < wc.WortAnzahl(); i++) cout << "Anzahl von : " << wc[i].Name << " beträgt : " << wc[i].Anzahl << endl; return 0; }Eine mögliche Ausgabe könnte so aussehen:
Alle gefunden Wörter in: meine.dat Anzahl von : Aenean beträgt : 5 Anzahl von : Aliquam beträgt : 3 Anzahl von : Curabitur beträgt : 5 Anzahl von : Curae beträgt : 2 Anzahl von : Donec beträgt : 5 Anzahl von : Duis beträgt : 3 Anzahl von : Etiam beträgt : 3 Anzahl von : Fusce beträgt : 6 Anzahl von : In beträgt : 1 Anzahl von : Integer beträgt : 5 Anzahl von : Lorem beträgt : 1 Anzahl von : Maecenas beträgt : 4 Anzahl von : Mauris beträgt : 1 Anzahl von : Morbi beträgt : 2 Anzahl von : Nulla beträgt : 4 Anzahl von : Nullam beträgt : 2 Anzahl von : Nunc beträgt : 3 Anzahl von : Pellentesque beträgt : 3 Anzahl von : Phasellus beträgt : 3 Anzahl von : Praesent beträgt : 1 Anzahl von : Proin beträgt : 2 Anzahl von : Quisque beträgt : 4 Anzahl von : Sed beträgt : 5 Anzahl von : Suspendisse beträgt : 5 Anzahl von : Ut beträgt : 3 Anzahl von : Vestibulum beträgt : 3 Anzahl von : Vivamus beträgt : 3Grüssle DC!
-
Und jetzt mal bissl eleganter:
string filetxt = ""; ifstream fileIn("..."); copy(istreambuf_iterator<char>(fileIn), istreambuf_iterator<char>(), back_inserter(filetxt)); // 1. Methode: Bustaben zählen: map<char, size_t> charCount; for (size_t i = 0; i < filetxt.size(); ++i) ++charCount[filetxt[i]]; cout << "Anzahl r's :" << charCount['r'] << "\nAnzahl a's :" << charCount['a'] << endl; // 2. Methode: Wörter zählen: boost::tokenizer<> toki(filetxt); map<string, size_t> wordCount; for (boost::tokenizer<>::iterator it=toki.begin(); it!=toki.end(); ++it) ++wordCount[*it]; cout << "Anzahl bla's :" << wordCount["bla"] << "\nAnzahl blub's :" << wordCount["blub"] << endl;Dauer: 2min 45s

-
@KasF Deinen Tipp finde ich gut, aber was daran eleganter sein sollte, weiß ich noch nicht, der Klassenansatz gefällt mir persönlich besser (von boost war ja noch nicht die Rede :)), da man das Konstrukt ('we can load anything, weapons, equipment and training simulations'
) erweitern kann.Allerdings hast du recht das der Tokenizer die gleiche Arbeit wie meine Funktion ZaehleWorte() eleganter erledigt, nur kann ich dort eben nicht mehr in den Code eingreifen und komplexe Bedingungen für die Wortgrenze zu definieren.
Auch istreambuf_iterator war mir in den Sinn gekommen, habe ihn aber wieder verworfen da ich nur den Teil im Speicher haben wollte den ich tatsächlich auch benötige und den Rest einfach verwerfe.
Ansonsten hätte ich auch so gemacht

So long and thank's for all the fish! (HG)
Grüssle DC!
-
@DeepCopy hey hey, thanx für die wirklich tolle Arbeit von dir, jetzt habe ich wenigstens mal ein Beispiel und kann alles genau analysieren und mein Lehrer freut sich auch, der hatte mich schon auf dem Kicker.
@KasF puh, das ist mir noch zu hoch
Trotzdem, tausend Dank an euch beiden!

Venti

-
Könnten wir die Ausgabe jetzt noch ein wenig, mit DeepCopy's Code, etwas edler machen?
z.Bsp.:
--------------------------------------------------------------------------------
Welches Wort suchen Sie?...eingabe Wort.....Die Anzahl Ihres gesuchten Wortes beträgt: .......Anzahl Wort....
--------------------------------------------------------------------------------
-
DeepCopy schrieb:
@KasF Deinen Tipp finde ich gut, aber was daran eleganter sein sollte, weiß ich noch nicht, der Klassenansatz gefällt mir persönlich besser
Das waren auch nur Codeschnipsel
das kann man immernoch schön verpacken.DeepCopy schrieb:
Allerdings hast du recht das der Tokenizer die gleiche Arbeit wie meine Funktion ZaehleWorte() eleganter erledigt, nur kann ich dort eben nicht mehr in den Code eingreifen und komplexe Bedingungen für die Wortgrenze zu definieren.
Meinst du die Wortgrenzen bei boost::Tokenizer ? Das Trennzeichen kann man dort auch definieren.
DeepCopy schrieb:
Auch istreambuf_iterator war mir in den Sinn gekommen, habe ihn aber wieder verworfen da ich nur den Teil im Speicher haben wollte den ich tatsächlich auch benötige und den Rest einfach verwerfe.
Benötigte ? Rest ? im Speicher ? Wo, wie was ?

Noch ein paar kleine Verbesserungsvorschläge zu deinem Code:
1.) Wieso ist der Stream aufm Heap und nicht aufm Stack ?
2.) Wieso reichst du deinen Member m_pfs in der Klasse rum ? Falls das Absicht ist, damit ZaehleWorte() auch mit anderen Quellen benutzt werden kann, stimmt hier was mit der Kohärenz nicht ?
3.) Dein ZaehleWorte() ist nicht unbedingt performant mit den ganzen Stream-Zugriffen.
4.) WortZurMap kann ein Einzeiler sein, siehe mein Code und die Referenz deiner Wahl.
5.) Für die Container-Kopieroperationen gibts es schöne Konstruktoren und std::copy.Und für *Nicht-Boost-Haben-Woller*
:class SimpleTokenizer { private: vector<string> toki; public: typedef vector<string>::const_iterator TokiIt; SimpleTokenizer(const string& str) { string blub = ""; stringstream sstr(str); while(sstr>>blub) toki.push_back(blub); } vector<string>::const_iterator begin() const { return toki.begin(); } vector<string>::const_iterator end() const { return toki.end(); } }; // 2. Methode: Wörter zählen: SimpleTokenizer toki(filetxt); map<string, size_t> wordCount; for (SimpleTokenizer::TokiIt it=toki.begin(); it!=toki.end(); ++it) ++wordCount[*it]; cout << "Anzahl bla's :" << wordCount["bla"] << "\nAnzahl blub's :" << wordCount["blub"] << endl;
-
ventilatores schrieb:
Könnten wir die Ausgabe jetzt noch ein wenig, mit DeepCopy's Code, etwas edler machen?
z.Bsp.:
--------------------------------------------------------------------------------
Welches Wort suchen Sie?...eingabe Wort.....Die Anzahl Ihres gesuchten Wortes beträgt: .......Anzahl Wort....
--------------------------------------------------------------------------------
Wenn du das nicht alleine schaffst, dann brauchst du auch gar nicht DC's Code zu benutzen.

-
***BlaBliBlub schrieb:
Wenn du das nicht alleine schaffst, dann brauchst du auch gar nicht DC's Code zu benutzen.
Äh wat?, ich benutz den schon und der läuft 1 A!
Ich habe doch mit meinen Projekt ganz anders Angefangen als DeepCopy und für mich
als super Anfänger ist das echt nicht leicht, dieses wie-was-wozu u.s.w.ein kleiner tipp und ne Erklärung wärn da super, nennt man neues modernes Lernen

gruß
Venti
-
So!, jetzt hast du dich bei 95% der registrierten User hier disqualifiziert
[Grund: mangelnde Eigeninitiative]
In deinem ersten Beitrag schien es noch danach auszusehen, dass du C++ lernen willst. Mit diesem Beitrag hier aber nicht mehr:ventilatores schrieb:
Könnten wir die Ausgabe jetzt noch ein wenig, mit DeepCopy's Code, etwas edler machen?
z.Bsp.:
--------------------------------------------------------------------------------
Welches Wort suchen Sie?...eingabe Wort.....Die Anzahl Ihres gesuchten Wortes beträgt: .......Anzahl Wort....
--------------------------------------------------------------------------------
[Newbie] Das ist cin und cout und bissl if und so, das steht in Kapitel 1 vieler Bücher [/Newbie]
Jetzt mal ernst, das sind wirklich Grund-Grundlagen. Ließ dir ein paar Tutorial dazu durch und versuchs selber. Copy&Paste bringt dir 5% Lerneffekt.
-
ja, is ja jut

muss das halt bis Freitag fertig haben und hab ja schon in der Zwischenzeit selber dran rum gebastelt also, passt schon

venti
p.s. 95% an Kumpels verloren die mir nich mehr helfen wollen, puhhh...fett
naja hoffe Deep Copy gehört zu den restlichen 5% die mir nochmal helfen
-
@ ventilatores:
KasF hat völlig Recht, mangelnde Eigeninitiative ist hier wirklich nicht gern gesehen. Es ist auch eine ziemliche Ausnahme, dass sich überhaupt jemand so viel Zeit wie DeepCopy nimmt. Von daher solltest du eher dankbar sein statt weitere Forderungen zu stellen.Wenn du dich etwas mit der Materie auseinandersetzt und versuchst, DeepCopys Code zu verstehen, solltest du auch relativ schnell in der Lage sein, das Programm selbstständig zu erweitern. Aber dazu ist halt C++-Theorie unumgänglich - du kannst schliesslich nicht für jedes kleine Problem im Forum nachfragen.
Nichts für ungut, aber ich hoffe, dir ist das nun etwas verständlicher geworden.

-
KasF schrieb:
1.) Wieso ist der Stream aufm Heap und nicht aufm Stack ?
2.) Wieso reichst du deinen Member m_pfs in der Klasse rum ? Falls das Absicht ist, damit ZaehleWorte() auch mit anderen Quellen benutzt werden kann, stimmt hier was mit der Kohärenz nicht ?
3.) Dein ZaehleWorte() ist nicht unbedingt performant mit den ganzen Stream-Zugriffen.
4.) WortZurMap kann ein Einzeiler sein, siehe mein Code und die Referenz deiner Wahl.
5.) Für die Container-Kopieroperationen gibts es schöne Konstruktoren und std::copy.zu 1)
Ich möchte das Objekt erst initialisieren/instanzieren wenn der Benutzer das Objekt tatsächlich braucht.zu 2)
Das hängt mit der von mir gewählten Richtline für Funktionssignaturen zusammen, ich erarbeite mir gerade eine neue Strategie für die Übergabe vom Elementdaten um Seiteneffekte zu minimieren.zu 3)
Kann ich nicht nachvollziehen, ich möchte nur den Speicher verbrauchen den ich auch benötige, dann bleibt mir nur die Möglichkeit immer das nächste Token aus dem Stream zu holen.zu 4)
Habe ich gesehen, über den operator[] das hat Vorteile und Nachteile. Vorteil die Map legt das Objekt in die Map wenn der Schlüssel nicht vorhanden ist und erhöht den integer, oder einfach nur den integer, dieses Konzept versagt aber bei komplexen Datentypen da es zwar eine Default-Initialisierung gibt aber ich komme so nicht an die "inneren Objekte", und der Code sollte als Anschauungsmaterial dienen wie man auch bei komplexen Datentypen arbeitet.zu 5)
Daran werde ich diese Woche noch arbeiten um das zu verbessern... danke für den Tip

@ventilatores
Vielen dank für Blumen, vielen Dank, wie lieb von Dir!
ventilatores schrieb:
Ich habe doch mit meinen Projekt ganz anders Angefangen als DeepCopy und für mich
als super Anfänger ist das echt nicht leicht, dieses wie-was-wozu u.s.w.
...
p.s. 95% an Kumpels verloren die mir nich mehr helfen wollen, puhhh...fett
naja hoffe Deep Copy gehört zu den restlichen 5% die mir nochmal helfen
Soweit es meine Zeit zulässt werde ich dir gerne Helfen, wenn du fragen zum Code hast stell sie einfach ich habe mir leider nicht die Mühe gemacht den Code zu Dokumentieren.

Für dein restliches Problem solltest du eigentlich nur absolute Grundlagen benötigen, so wie ventilatores und Nexus es gesagt haben, wirklich. Ist nicht mehr als ein paar If's und ein bisschen cin und cout. Versuche den Code von mir oder KasF zu verstehen, dann solltest du dein Prob. mit links hinkriegen. Und wenn du nich weiter kommst, dein Stück fehlerhaften Code posten und hoffen das einer hilft.

Grüssle DC!
-
hi,
warum wird nach dem zugelassenen Zugang die Schleife nicht weiter geführt und die Datei nicht durchsucht?
Habe jetzt einiges probiert und komme nicht weiter.greetz
Venti#include <iostream> #include "wc.hpp" using namespace std; int pnummer, code; int personalnummer = 1999; int geheimcode = 1212; int main() { string datei = "collection.txt"; CWortCount wc; cout << "Geben Sie Ihre Personalnummer ein: "; cin >> pnummer; cout << "\nGeben Sie Ihren Zugangscode ein: "; cin >> code; if (!wc.OeffneDatei(datei), (pnummer == personalnummer) && (code == geheimcode)) { cout << "\nZutritt gewaehrt!\n"; return 0; } if ((pnummer == personalnummer) && !(code == geheimcode)) { cout << "\nFalscher Zugangscode!\n"; return 0; } if (!(pnummer == personalnummer) && (code == geheimcode)) { cout << "\nFalsche Personalnummer!\n"; return 0; } if (!(pnummer == personalnummer) && !(code == geheimcode)) { cout << "\nFalsche Zugangsdaten!\n"; return 0; } cout << "Kann die Datei nicht oeffnen : " << datei << endl; cout << "Bitte Warten, analysiere die Datei " << endl; wc.SortWord(); cout << "Alle gefunden Wörter in: " << datei << endl; for(int i = 0; i < wc.WortAnzahl(); i++) cout << "Anzahl von " << wc[i].Name << " betraegt : " << wc[i].Anzahl << endl; return 0; }
-
if (!wc.OeffneDatei(datei), (pnummer == personalnummer) && (code == geheimcode)) // <- hier darf kein , stehen { cout << "\nZutritt gewaehrt!\n"; return 0; <- damit wird dein Programm beendet! Das ist falsch }Diese Anweisung macht nicht viel Sinn, du must das Öffnen separat ausführen und überprüfen!
Besser:
if (!wc.OeffneDatei(datei)) { cout << "Kann die Datei nicht oeffnen : " << datei << endl; return 0; } // ab hier alle if (...) aber ohne !wc.OeffneDatei(datei) if (!(pnummer == personalnummer) && (code == geheimcode)) { cout << "\nFalscher Zugangscode!\n"; return 0; // <-- Beenden ok, die personalnummer war falsch } // ... usw. // diese zeile ganz nach oben... // cout << "Kann die Datei nicht oeffnen : " << datei << endl; cout << "Bitte Warten, analysiere die Datei " << endl; // ab hier weiter wie bisher.. wc.SortWord(); cout << "Alle gefunden Wörter in: " << datei << endl; // ... usw.
-
ahhh, alles klar thanx DC gut erklärt.
ups... palim palim, da is'n ERROR!
--->jetzt geht die PW Abfrage nicht mehr,
also man kommt auch mit falsche Zugangsdaten rein.um gottes Willen!

gruß
Venti
-
DeepCopy schrieb:
zu 1)
Ich möchte das Objekt erst initialisieren/instanzieren wenn der Benutzer das Objekt tatsächlich braucht.Ok, da aber dein m_pfs nur lokal in OeffneDatei, hättest du es dort auch aufm Stack erzeugen können. Würde dann auch erst bei Benutzung der Funktion generiert werde. Desweiterhin tust du zwei Sachen bzw. eine Sache in OeffneDatei ohne das es der Funkionsname andeutet, nämlich ZaehleWorte aufrufen. Wieder etwas, was in Richtung Orthogonalität/Kohärenz geht.
DeepCopy schrieb:
zu 3)
Kann ich nicht nachvollziehen, ich möchte nur den Speicher verbrauchen den ich auch benötige, dann bleibt mir nur die Möglichkeit immer das nächste Token aus dem Stream zu holen.Letzendlich holst du doch ehe alles raus, insofern wäre es performanter mehrere Bytes auszulesen, statt jedes mal 1-Byte aus dem Stream zu lesen. Alles in einem Rutsch einlesen und dann im Hauptspeicher byteweise arbeiten sollte schneller sein.
DeepCopy schrieb:
zu 4)
Habe ich gesehen, über den operator[] das hat Vorteile und Nachteile. Vorteil die Map legt das Objekt in die Map wenn der Schlüssel nicht vorhanden ist und erhöht den integer, oder einfach nur den integer, dieses Konzept versagt aber bei komplexen Datentypen da es zwar eine Default-Initialisierung gibt aber ich komme so nicht an die "inneren Objekte", und der Code sollte als Anschauungsmaterial dienen wie man auch bei komplexen Datentypen arbeitet.Naja, wir habens hier aber mit primitiven Typen zu tun

DeepCopy schrieb:
zu 5)
Daran werde ich diese Woche noch arbeiten um das zu verbessern... danke für den Tip

Jop, habe mal irgendwo aufgeschnappt das du anfängst STL-Luft zu schnuppern

-
@DeepCopy...hi
ich habe jetzt ein wenig rumprobiert und den kleinen Fehler gefunden.
[cpp]// Alt: if (!(pnummer == personalnummer) && (code == geheimcode)) // Neu: if ((pnummer [b]!=[/b] personalnummer) [b]|| [/b](code [b]!=[/b] geheimcode)) ! <--- entfällt gänzlich [/cpp]gruß
Ventip.s. Ich möchte mich nochmals für die großartige Hilfe von DC bedanken, der mir durch sein gezeigtes Beispiel C++ näher gebracht hat.
.
-
die beiden sollten eigentlich identische ergebnisse liefern...
!( A && B ) == !A || !B
-
KasF schrieb:
Wieder etwas, was in Richtung Orthogonalität/Kohärenz geht.
Wenn Orthogonalität/Kohärenz für ungewollte Abhängigkeit oder unerwünschte Seiteneffekte steht, dann muss ich dir wirklich recht geben, das ist mir bei der Entwicklung der Klassen-Schnittstelle auch aufgefallen, aber... mal ehrlich... nach all der Arbeit hatte ich einfach keine Lust mehr da noch etwas zu ändern und den Aufruf zu ändern, zumal es sich hierbei eher um ein Beispiel handelt, trotzdem Danke für deinen Hinweis.

It0101 schrieb:
!( A && B ) == !A || !B
@ventilatores Das was It0101 hier aufzeigt ist der Satz von De Morgan der besagt, wenn ich einen Ausdruck wie '(A && B)' negiere, also '!(A && B)' dann kann ich den Ausdruck immer so umformen das die Aussagen 'A', 'B' negiert werden also '!A', '!B' und der Operator des Ausdrucks && umgekehrt wird, also aus '&&' wird '||' und umgekehrt, das für dann zu '(!A || !B)'.