Textdatei einlesen und trennen...
-
Vielleicht wäre für dich auch noch interessant zu wissen, das der Container std::map. dir das speichern von Schlüssel-Wertpaaren erleichtert.Dann kannst du da auch eventuell deinen Knoten mit seiner ID abspeicher.
-
ich komme nicht weiter

ich kann jetzt meine knoten und kanten erstellen, allerdings weiß ich nicht wie ich diese jetzt sinnvoll in einen container (map???, vektor + linklist???) speichern soll...hier mal was ich bisher habe:
// importExport.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung. // #include <stdafx.h> #include <cstdlib> #include <iostream> #include <fstream> #include <sstream> #include <map> #include <string> #include "knoten.h" #include "pfeil.h" using namespace std; ifstream inFileKnoten; ifstream inFilePfeil; /*ofstream inFileOut;*/ ofstream outFile; /*takes filename from stdin and trys to open file*/ void openFile(); /*writes output file*/ void writeToFile(); void stringToInt(string &); void trimString(string &); int main(){ openFile(); writeToFile(); getchar(); return 0; } void openFile(){ inFileKnoten.open("knoten.txt"); if(inFileKnoten.fail()){ char c; cerr << "Fehler: Datei knoten.txt konnte nicht geöffnet werden\n" << endl; cin >> c; } inFilePfeil.open("pfeile.txt"); if(inFilePfeil.fail()){ char c; cerr << "Fehler: Datei pfeile.txt konnte nicht geöffnet werden\n" << endl; cin >> c; } } void writeToFile(){ //cout << "Ich schreibe die TextDatei..." << endl; //creation of the knoten-------------------------------- string c; while(getline(inFileKnoten,c)){ //remove spaces trimString(c); //the semikolon as an indicator int pos = c.find(";"); //find the word in the stringline string name = c.substr(pos+1); //find the number in the stringline //to-DO change the cast!!! int number = atoi(c.substr(0,pos).c_str()); //create the knoten knoten myFirstKnoten = knoten(number,name); cout << "my knoten: " << myFirstKnoten.print() << endl; } //END creation of the knoten-------------------------------- //creation of the pfeil-------------------------------- string s; while(getline(inFilePfeil,s)){ trimString(s); //the semikolon as an indicator int pos = s.find(";"); int pos2 = s.find_last_of(";"); //find alhpa and omega and weight //to-DO change the cast!!! int alpha = atoi(s.substr(0,pos).c_str()); int omega = atoi(s.substr(pos + 1, pos2).c_str()); int gewicht = atoi(s.substr(pos2 + 1, s.length()).c_str()); pfeil myPfeil = pfeil(alpha, omega, gewicht); cout << "my kanten: " << alpha << " " << omega << " " << gewicht << endl; } //END creation of the knoten-------------------------------- } //removes the spaces out of the string void trimString(string &str) { for (int i=0; i < str.length(); i++) if (str[i] == ' ') { str.erase(i,1); i--; } }und hier die pfeil(kanten) und die knoten klassen, die jeweils einen header dazu haben
#include "stdafx.h" #include "pfeil.h" #include <cstdio> using namespace std; int alpha; int omega; int gewicht; pfeil::pfeil() :alpha(0), omega(0), gewicht(0) { } pfeil::pfeil(int alp, int om, int gew) :alpha(alp), omega(om), gewicht(gew) { }#include "stdafx.h" #include "knoten.h" #include <cstdio> #include <string> #include <sstream> using namespace std; knoten::knoten() :index(0), name("0") { } knoten::knoten(int ind, string nam) :index(ind), name(nam) { } string knoten::print() { ostringstream temp; temp << index; string zahlenString = temp.str(); return zahlenString + "; " + name; }bitte um hilfe... weiß wirklich nicht wie es weitergeht
-
Hallo Palme,
falls Du Informationen aus einer Datei liest, so solltest Du immer die Funktionen des std::istream dafür nutzen. Ein Lesen mit getline und anschließendem Auseinanderfiseln des strings ist keine Lösung. Man versieht dafür jede Klasse/Struktur, die ausgeben und/oder eingelesen wird, mit einem Streamingoperator. Also in Deinem Fall:
#include <string> #include <iosfwd> // -- Knoten-Klasse class knoten { public: knoten(); knoten(int ind, const std::string& nam); friend std::istream& operator>>( std::istream& in, knoten& k ); friend std::ostream& operator<<( std::ostream& out, const knoten& k ); // ersetzt 'print' private: int index; std::string name; }; // -- Pfeil-Klasse class pfeil { public: pfeil(); pfeil(int alp, int om, int gew); // -- Pfeil einlesen & ausgeben friend std::istream& operator>>( std::istream& in, pfeil& p ); friend std::ostream& operator<<( std::ostream& out, pfeil& p ); private: int alpha; int omega; int gewicht; };Die Implementierung sähe dann so aus:
// -- Helferlein zum Überlesen einzelner Zeichen template< char C > std::istream& Char( std::istream& in ) { char c; if( in >> c && c != C ) in.setstate( std::ios_base::failbit ); return in; } // -- Knoten-Klasse knoten::knoten() : index(0), name("0") {} knoten::knoten(int ind, const std::string& nam) : index(ind), name(nam) {} // -- Ausgabe (ersetzt: string knoten::print() ) std::ostream& operator<<( std::ostream& out, const knoten& k ) { return out << k.index << "; " << k.name; } // -- Einlesen eines Knotens std::istream& operator>>( std::istream& in, knoten& k ) { return getline( in >> k.index >> Char<';'> >> std::ws, k.name ); } // -- Pfeil pfeil::pfeil() : alpha(0), omega(0), gewicht(0) {} pfeil::pfeil(int alp, int om, int gew) : alpha(alp), omega(om), gewicht(gew) {} std::istream& operator>>( std::istream& in, pfeil& p ) { return in >> p.alpha >> Char<';'> >> p.omega >> Char<';'> >> p.gewicht; } std::ostream& operator<<( std::ostream& out, pfeil& p ) { return out << p.alpha << " " << p.omega << " " << p.gewicht; }Da ich gerade an einem Projekt sitze, das nur deshalb dreimal so viel Aufwand erfordert wie es eigentlich erfordern sollte und das alles nur weil der Programmierer globale Variablen verwendet ...
hier noch mal der ernst gemeinte Hinweis: vermeide globale Variablen!
palmdale schrieb:
.. bin ganz neu auf dem gebiet c++ und hab vorher nur mit anderen sprachen zu tu gehabt.
.. dann solltest Du das auch wissen; das gilt für alle Programmiersprachen.
Das Öffen der Dateien kannst Du ruhig in das main nehmen, dann sieht es so aus:
#include <fstream> #include <iostream> void readFromFile( std::istream& fileKnoten, std::istream& filePfeil ); using namespace std; int main() { ifstream inFileKnoten( "knoten.txt" ); if( !inFileKnoten.is_open() ) { char c; cerr << "Fehler: Datei knoten.txt konnte nicht geöffnet werden\n" << endl; cin >> c; return -1; } ifstream inFilePfeil( "pfeile.txt" ); if( !inFilePfeil.is_open() ) { char c; cerr << "Fehler: Datei pfeile.txt konnte nicht geöffnet werden\n" << endl; cin >> c; return -1; } readFromFile( inFileKnoten, inFilePfeil ); // Lesen aus den Dateien getchar(); return 0; }und dank der Vorarbeit wird jetzt das Lesen ganz einfach:
void readFromFile( std::istream& fileKnoten, std::istream& filePfeil ) { for( knoten k; fileKnoten >> k; ) { cout << "my knoten: " << k << endl; } for( pfeil p; filePfeil >> p; ) { cout << "my kanten: " << p << endl; } }palmdale schrieb:
ich komme nicht weiter

ich kann jetzt meine knoten und kanten erstellen, allerdings weiß ich nicht wie ich diese jetzt sinnvoll in einen container (map???, vektor + linklist???) speichern soll...Das kann man erst dann entscheiden, wenn bekannt ist, was Du mit den Knoten und Kanten vor hast. Erst müssen die Anforderungen klar sein, dann kann man sagen, wie das Programm aussehen soll.
Was ist der Zweck Deines Programms? was soll es tun? was ist der Output?
Gruß
Werner
-
wow danke für deine ausführliche antwort, werde mir das zu herzen nehmen und meinen code nochmal überarbeiten. jetzt zu dem graphen.
im allgemeinen möchte ich erstmal einen container der kanten und knoten speichert. dieser soll dann folgendes ausgeben:
• Ermittlung des Ausgangsgrades g+(u) und Eingangsgrades g+(u) eines wählbaren
Knotens
• Ermittlung der Nachbarn (Vorgänger und Nachfolger) eines wählbaren Knotensecht nochmal vielen dank für die tipps... ich such mich bald wund im internet um was gescheites zu finden. werde jetzt mein code nochmal überarbeiten.
//edit: wenn ich deinen code benutze gibt es einen fehler das der knoten nicht erkannt wird, wenn du den erstellen willst...
error C2065: 'knoten': nichtdeklarierter Bezeichner (in der readFromFile methode)
wie verbinde ich denn diese beiden klassen, damit der erkannt wird?diese sprache macht mir keinen spaß :(, aber wenigstens gibt es leute die helfen
-
palmdale schrieb:
• Ermittlung des Ausgangsgrades g+(u) und Eingangsgrades g+(u) eines wählbaren
Knotens
• Ermittlung der Nachbarn (Vorgänger und Nachfolger) eines wählbaren KnotensOh - ja dann ist kein Container aus dem C++ Standard wirklich geeignet - schau Dir mal die boost.graph-Librarie an. Die Alternative bestände darin, Dir selbst etwas aus den C++-Containern zusammenzubauen.
Jeder Knoten enthält dann schon einen Container (vector oder list) mit Zeigern auf seine Nachbarn und dem dazugehörigen Gewicht. Die Knoten selbst könnte man dann in eine std::map abladen, aber wieder nur als Pointer (besser boost.shared_ptr) da sie nicht kopierbar sein dürfen, damit ihre Vorgänger sie sicher wiederfinden.
palmdale schrieb:
ich such mich bald wund im internet um was gescheites zu finden. werde jetzt mein code nochmal überarbeiten.
.. es gibt furchtbar wenig zum Thema IO in C++ - warum auch immer.
Gruß
Werner
-
die aufgabe besteht leider darin das ich diese boost.graph-Librarie nicht nehmen kann...

//edit: hab jetzt deins zum laufen gebracht und beginne zu verstehen was du da machst aufgrund meiner kenntnisse in anderen sprachen.
-
palmdale schrieb:
//edit: wenn ich deinen code benutze gibt es einen fehler das der knoten nicht erkannt wird, wenn du den erstellen willst...
error C2065: 'knoten': nichtdeklarierter Bezeichner (in der readFromFile methode)
wie verbinde ich denn diese beiden klassen, damit der erkannt wird?Dann hast Du das #include "knoten.h" dort vergessen.
palmdale schrieb:
diese sprache macht mir keinen spaß :(, aber wenigstens gibt es leute die helfen
.. der Spaß kommt mit der Zeit

Gruß
Werner
-
.. der Spaß kommt mit der Zeit

darauf warte ich jetzt aber schon ne ganze weile, in den anderen (neueren) sprachen hab ich mich sehr schnell zurecht gefunden, aber dieses c++ ist für mich n bisschen wie ein krampf.
hab deine sachen jetzt zum laufen gebracht und beginne zu verstehen, wie du das aufgebaut hast. vielen vielen dank nochmal dafür (ich hoffe ich halte dich nicht von deinem derzeitigen projekt ab).
hast du ne idee wie man das löst? ich hatte zuerst an einen vektor gedacht, der eine link liste in sich trägt, eine map zu implementieren hab ich versucht, allerdings bisher ohne erfolg, da ich das mit dem pair nicht verstanden habe...
-
palmdale schrieb:
.. der Spaß kommt mit der Zeit

darauf warte ich jetzt aber schon ne ganze weile, in den anderen (neueren) sprachen hab ich mich sehr schnell zurecht gefunden, aber dieses c++ ist für mich n bisschen wie ein krampf.
Hallo Palme,
C++ ist eine schwierige Sprache, und sie ist anders.palmdale schrieb:
hab deine sachen jetzt zum laufen gebracht und beginne zu verstehen, wie du das aufgebaut hast. vielen vielen dank nochmal dafür (ich hoffe ich halte dich nicht von deinem derzeitigen projekt ab).
.. gucke gerade Song Contest - da bin ich geistig nicht ausgelastet.

palmdale schrieb:
hast du ne idee wie man das löst? ich hatte zuerst an einen vektor gedacht, der eine link liste in sich trägt, eine map zu implementieren hab ich versucht, allerdings bisher ohne erfolg, da ich das mit dem pair nicht verstanden habe...
Ein pair ist einfach eine Struktur mit zwei Membern (first und second) nicht mehr und nicht weniger. Bei einer std::map wird der Member first als Schlüssel benutzt, um das Element (also das ganze pair) zu identifizieren. In second kann dann stehen was will.
Um den Graph zu realisieren würde ich Dir im Prinzip folgendes vorschlagen. Rüste zunächst Deine knoten-Klasse auf, die wird später Teil des Graphs.
// -- Knoten-Klasse class knoten { public: typedef knoten* KnotenPtr; // evt. typedef boost::shared_ptr< knoten > KnotenPtr; struct Next { Next( KnotenPtr nachfolger, int gewicht ) : m_nachfolger( nachfolger ), m_gewicht( gewicht ) {} KnotenPtr m_nachfolger; int m_gewicht; }; void Nachfolger( KnotenPtr next, int gewicht ); int Index() const { return index; } // -- usw. wie vorher private: std::vector< Next > m_nachfolger; // erfordert #include <vector>Damit bekommt jeder Knoten einen Container in dem alle Nachfolger mit den Gewichten der Kanten stehen.
In der Methode readFromFile
typedef std::map< int, knoten::KnotenPtr > Graph; // erfordert #include <map> Graph readFromFile( std::istream& fileKnoten, std::istream& filePfeil );- Achtung - neuer Returntyp - muss der Graph zusammen gebaut werden:
Graph readFromFile( std::istream& fileKnoten, std::istream& filePfeil ) { Graph graph; // -- erst alle Knoten einlesen, die Liste aller Anchfolger ist jeweils noch leer for( knoten k; fileKnoten >> k; ) { graph[ k.Index() ] = new knoten( k ); cout << "neuer knoten: " << k << endl; } // -- dann die Kanten einlesen und die Knoten verbinden for( pfeil p; filePfeil >> p; ) { // erst den Ausgangsknoten suchen Graph::iterator alpha = graph.find( p.alpha ); if( alpha == graph.end() ) { cerr << "Ausgangsknoten-Knoten " << p.alpha << " nicht vorhanden " << endl; break; } // .. dann den Eingangsknoten suchen Graph::iterator omega = graph.find( p.omega ); if( omega == graph.end() ) { cerr << "Eingangs-Knoten " << p.omega << " nicht vorhanden " << endl; break; } alpha->second->Nachfolger( omega->second, p.gewicht ); } return graph; }Achtung -ich habe die Member von 'pfeil' public gemacht, da diese Klasse nur zum Einlesen genutzt wird und es so einfacher wird.
Bevor ich lange erkläre - frag' einfach was Du nicht verstehst.
Gruß
Werner
-
tausend dank ich medietiere darüber und werde jetzt schlafen gehen. morgen werd ich bestimmt dann fragen stellen
wirklich danke für deine zeit.
-
so jetzt ist alles eingebunde, ich bekomme aber leider eine fehlermeldung mit der ich wenig anfangen kann...
Verweis auf nicht aufgelöstes externes Symbol ""public: void __thiscall knoten::Nachfolger(class knoten *,int)" (?Nachfolger@knoten@@QAEXPAV1@H@Z)" in Funktion ""class std::map<int,class knoten *,struct std::less<int>,class std::allocator<struct std::pair<int const ,class knoten *> > > __cdecl readFromFile(class std::basic_istream<char,struct std::char_traits<char> > &,class std::basic_istream<char,struct std::char_traits<char> > &)" (?readFromFile@@YA?AV?less@H@std@@V?pair@$$CBHPAVknoten@@@std@@@3@@std@@AAV?char_traits@D@std@@@2@0@Z)"
ich verstehe auch nicht wo du die neuen methoden wie nachfolger in der knten class implementierst. bisher haste die ja nur in den header geschrieben, richtig?
hier mal meine header und meine knoten klasse (hab den header in eine extra knoten.h geschrieben):
knoten.h
#include "stdafx.h" #include <string> #include <iosfwd> #include <vector> using namespace std; // -- Knoten-Klasse class knoten { public: knoten(); knoten(int ind, const string& nam); typedef knoten* KnotenPtr; struct Next { Next( KnotenPtr nachfolger, int gewicht ) : m_nachfolger( nachfolger ), m_gewicht( gewicht ) {} KnotenPtr m_nachfolger; int m_gewicht; }; void Nachfolger( KnotenPtr next, int gewicht ); int Index() const { return index; } friend istream& operator>>( istream& in, knoten& k ); friend ostream& operator<<( ostream& out, const knoten& k ); private: int index; string name; std::vector< Next > m_nachfolger; };knoten.cpp
#include "stdafx.h" #include <string> #include <iosfwd> #include "knoten.h" using namespace std; // -- Helferlein zum Überlesen einzelner Zeichen template< char C > istream& Char( istream& in ) { char c; if( in >> c && c != C ) in.setstate( ios_base::failbit ); return in; } // -- Knoten-Klasse knoten::knoten() : index(0), name("0") {} knoten::knoten(int ind, const string& nam) : index(ind), name(nam) {} // -- Ausgabe eines Knotens ostream& operator<<( ostream& out, const knoten& k ) { return out << k.index << "; " << k.name; } // -- Einlesen eines Knotens istream& operator>>( istream& in, knoten& k ) { return getline( in >> k.index >> Char<';'> >> ws, k.name ); }und hier noch der vollständigkeit wegen die main
// alg_graph.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung. // #include "stdafx.h" #include <fstream> #include <iostream> #include "knoten.h" #include "pfeil.h" #include <map> using namespace std; typedef std::map< int, knoten::KnotenPtr > Graph; Graph readFromFile( std::istream& fileKnoten, std::istream& filePfeil ); int main() { ifstream inFileKnoten( "knoten.txt" ); if( !inFileKnoten.is_open() ) { char c; cerr << "Fehler: Datei knoten.txt konnte nicht geöffnet werden\n" << endl; cin >> c; return -1; } ifstream inFilePfeil( "pfeile.txt" ); if( !inFilePfeil.is_open() ) { char c; cerr << "Fehler: Datei pfeile.txt konnte nicht geöffnet werden\n" << endl; cin >> c; return -1; } readFromFile( inFileKnoten, inFilePfeil ); // Lesen aus den Dateien getchar(); return 0; } Graph readFromFile( std::istream& fileKnoten, std::istream& filePfeil ) { Graph graph; // -- erst alle Knoten einlesen, die Liste aller Anchfolger ist jeweils noch leer for( knoten k; fileKnoten >> k; ) { graph[ k.Index() ] = new knoten( k ); cout << "neuer knoten: " << k << endl; } // -- dann die Kanten einlesen und die Knoten verbinden for( pfeil p; filePfeil >> p; ) { // erst den Ausgangsknoten suchen Graph::iterator alpha = graph.find( p.alpha ); if( alpha == graph.end() ) { cerr << "Ausgangsknoten-Knoten " << p.alpha << " nicht vorhanden " << endl; break; } // .. dann den Eingangsknoten suchen Graph::iterator omega = graph.find( p.omega ); if( omega == graph.end() ) { cerr << "Eingangs-Knoten " << p.omega << " nicht vorhanden " << endl; break; } alpha->second->Nachfolger( omega->second, p.gewicht ); } return graph; }
-
du hast nur vergessen die fkt zu definieren:
void Nachfolger( KnotenPtr next, int gewicht );Und deshalb geht das kompilieren auch fehlerfrei und erst beim Linken am Ende sagt dir der Linker die Fehlermeldung:
Verweis auf nicht aufgelöstes externes Symbol ""public: void __thiscall knoten::Nachfolger(class knoten *,int)" (?Nachfolger@knoten@@QAEXPAV1@H@Z)" in Funktion ""class std::map<int,class knoten *,struct std::less<int>,class std::allocator<struct std::pair<int const ,class knoten *> > > __cdecl readFromFile(class std::basic_istream<char,struct std::char_traits<char> > &,class std::basic_istream<char,struct std::char_traits<char> > &)" (?readFromFile@@YA?AV?less@H@std@@V?pair@$$CBHPAVknoten@@@std@@@3@@std@@AAV?char_traits@D@std@@@2@0@Z)"
Das nicht mehr fette ist nicht mehr ganz so interessant... Da sagt er dir z.Bsp. in welcher Fkt die nicht definierte aufgerufen werden soll - das sieht hier nur bissl extrem unübersichtlich aus, weil er alle typen vollständig hinschreibt...
bb
-
jup daran lags, super!
wie kann ich das denn jetzt ausgeben das ich zu einem beliebigen punkt alle kanten bekomme? muss glaub ich nochmal über deinen code schauen, ist ein bischen viel auf einmal...
-
palmdale schrieb:
Verweis auf nicht aufgelöstes externes Symbol ""public: void __thiscall knoten::Nachfolger(class knoten *,int)"
Hallo Palme,
ich hatte vergessen, Dir die Implementierung dieser Methode zu posten. In Knoten.cpp muss noch eingefügt werden:
void knoten::Nachfolger( KnotenPtr nachfolger, int gewicht ) { m_nachfolger.push_back( Next( nachfolger, gewicht ) ); }D.h. aus dem Nachfolger-Knoten (bzw. dem Pointer auf diesen) und dem Gewicht wird ein Objekt der Struktur 'Next' erzeugt und das wird mit in den vector der Nachfolger aufgenommen (push_back).
palmdale schrieb:
wie kann ich das denn jetzt ausgeben das ich zu einem beliebigen punkt alle kanten bekomme? muss glaub ich nochmal über deinen code schauen, ist ein bischen viel auf einmal...
inden Du z.B. die Ausgabe-Funktion des Knotens aufmotzt. Ein Knoten ist nun mehr als ein Index und der Straßenname. Also in knoten.cpp:
// -- Ausgabe (ersetzt: string knoten::print() ) std::ostream& operator<<( std::ostream& out, const knoten& k ) { out << k.index << "; " << k.name; if( !k.m_nachfolger.empty() ) out << "\n"; // Nachfolger in die nächste Zeile schreiben // Bem.: erfordert #include <algorithm> (copy) und #include <iterator> (ostream_iterator) und Streaming-Operator für Next copy( k.m_nachfolger.begin(), k.m_nachfolger.end(), std::ostream_iterator< knoten::Next >( out, "; " ) ); return out; }und weil jetzt die Ausgabe von knoten::Next benötigt wird, auch in knoten.cpp
// -- Streaming-Operator für Next std::ostream& operator<<( std::ostream& out, const knoten::Next& kante ) { return out << "(" << kante.m_gewicht << ")-> " << kante.m_nachfolger->Index(); }und im Header (knoten.h) noch die Deklaration des Streamingoperators
struct Next { Next( KnotenPtr nachfolger, int gewicht ) : m_nachfolger( nachfolger ), m_gewicht( gewicht ) {} friend std::ostream& operator<<( std::ostream& out, const knoten::Next& kante ); // usw. ...So kannst Du auch den Graphen nach dem Einlesen wieder ausgeben - im main() zu ändern:
Graph graph = readFromFile( inFileKnoten, inFilePfeil ); // Lesen aus den Dateien for( Graph::iterator i = graph.begin(); i != graph.end(); ++i ) { cout << *(i->second) << endl; }.. was dann noch fehlt ist das Aufräumen im Speicher! Kannst Du boost.shared_ptr benutzen?
Gruß
Werner
-
eigentlich nur native c++, d.h. so viel wie möglich selber beasteln

-
Du kannst natürlich auch einen
shared_ptrselber basteln.
Vielleicht nicht mit der Komplexität wie bei Boost, aber die wichtigen Dinge wie Referenzzählung sollten nicht allzu schwer zu implementieren sein.
-
hier kann geschlossen werden, vielen dank allen, die mir geholfen haben... konnte mein projekt dadurch erfolgreich abschliessen... (besonderen dank an werner für die geopferte zeit (auch während des grand prix ;)))