Auslesen von komplexen Zahlen aus einer Datei
-
Gugelmoser schrieb:
Werner Salomon schrieb:
Schade - und ich hatte mir wirklich Mühe gegeben, es eben nicht kompliziert zu beschreiben - eben Schritt für Schritt.
Also ich für meinen Teil kann bei deinen Lektionen viel lernen (vor allem Richtung abstrakter Denkweise) und ich denke andere auch (inklusive meines Profs :D). Also falls du noch Lust hast die weiteren Lektionen zu posten, würde ich mich zumindest sehr freuen.

Danke

dann mache ich weiter - zwei Lektionen habe ich noch. Und dann muss man mal sehen.Hallo an alle, die es interessiert,
Ich muss noch mal betonen, dass diese Art der Lösung vorrangig dafür gedacht ist, zu zeigen wie es geht. In der Praxis würde man sicher einiges zusammenfassen. Allerdings würde ich diese Art der Lösung immer der bevorzugen, die ich in meinem letzten Beitrag an Javun gepostet habe. Bei der (anscheinend komplizierten) Lösung bin ich mir eher sicher, dass sie funktioniert, und wenn nicht, lassen sich Probleme schneller identifizieren, weil ja alles einzeln testbar ist.
Aber nun zum 'j'.Im Grunde ist das die einfachste Lektion - es geht letztlich nur darum, dass nächste Zeichen zu Lesen und wenn's kein 'j' ist, den Stream auf Fehler zu setzen. Für mich wäre es am einfachsten auf Char<> zu verweisen und den Streaming-Operator des ComplexReaders wie folgt zu ändern:
friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value { double real, imag; PlusMinus plus; if( in >> real >> plus >> Char<'j'> >> imag ) cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag ); return in; }jetzt gibt es immer einen Fehler, falls das 'j' in der komplexen Zahl fehlt. Damit wird die Eingabesicherheit erhöht. Wenn Formatfehler vorliegen merkt man es ggf. früher.
Natürlich kann man das auch als Teil des ComplexReader implementieren - nur der Vollständigkeit halber:
// -- Lektion 4 #include <iostream> #include <complex> template< typename T > struct ComplexReader { struct PlusMinus { friend std::istream& operator>>( std::istream& in, PlusMinus& pm ) { if( in >> pm.m_op ) { if( pm.m_op != '+' && pm.m_op != '-' ) in.setstate( std::ios_base::failbit ); } return in; } bool positive() const { return m_op == '+'; } private: char m_op; }; friend std::istream& imagChar( std::istream& in ) { char c; if( in >> c && c != 'j' ) // <== hier steht das 'j' in.setstate( std::ios_base::failbit ); return in; } typedef T value_type; ComplexReader( std::complex< value_type >& target ) : m_target( target ) {} friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value { double real, imag; PlusMinus plus; if( in >> real >> plus >> imagChar >> imag ) cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag ); return in; } private: std::complex< value_type >& m_target; }; template< typename T > ComplexReader< T > read_complex( std::complex< T >& z ) { return ComplexReader< T >( z ); } int main() { using namespace std; cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl; complex< double > z; while( cin >> read_complex( z ) ) cout << "gelesen: " << z << endl; return 0; }ein Test zeigt
eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 3+j9 gelesen: (3,9) -9.1234E-009-j4.009E-1 gelesen: (-9.1234e-009,-0.4009) 3+i9 -> LesefehlerDas Programm besteht jetzt auf dem 'j' und das ist auch gut so, denn vorher wäre eine Eingabe wie -5+12.1 als (-5,2.1) ohne Erkennen eines Fehlers akzeptiert worden!
In Lektion 5 geht es um den optionalen Imaginärteil.
-
Danke erstmal für die Hilfe. Leider muss ich sagen, dass Dein Code auch nicht funktioniert.
Er bricht genau an diesem Übergang
-2.32123546894e-007-j3.26888151276e-009 -1.99295822081e-007-j2.80658484281e-009 -1.74571227184e-007-j2.45840055798e-009 -1.55279183547e-007-j2.18672021518e-009 -1.39806488024e-007-j1.96882586959e-009 -1.27121132199e-007-j1.79018425527e-009 +1.60790664118e-007 +1.81016196142e-007 +2.07106698463e-007 +2.42045708361e-007ab, d.h. f.fail tritt ein...
Wenn ich das
if( f.fail() ) // auf Fehler prüfen break;rausnehme, dann sind ab der oben genannten Stelle alle folgenden complex Zahlen
(-2.32123546894e-007,0)Ich finde das schon sehr ungewöhnlich...
Nachtrag: Es muss am f.unget() liegen! Wenn ich dies rausnehme, dann stimmen viele Ergebnisse wieder, wiederrum werden andere falsch. Welche andere Möglichkeit gibt es?
-
.. auf jeden Fall die letzte für heute,
Was ich bis jetzt einfach verschwiegen habe, ist die Tatsache, dass zwischen den einzelnen Teile der Komplexen Zahl beliebig viele Whitespace Character (Space, Tab, LF) stehen dürfen. Also würde von dem hier vorgestellten ComplexReader folgendes einwandfrei gelesen:
eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 12.5 - j 0.002 gelesen: (12.5,-0.002)Um zu definieren wie man einen optionalen Imaginärteil erkennt, muss das Format genau definiert sein. Oben wurde der Vorschlag gemacht, auf das j los zu gehen. Das hat aber zum einen den Nachteil, das das 'j' nicht das erste Zeichen eines Imaginärteils ist, weil davor steht noch der Operator, und zum anderen kann man doch erwarten, dass ein Imaginärteil auch allein stehen kann. Wenn der Realteil allein vorkommt, warum dann nicht auch der Imaginärteil?
Beispiel:-1.23-j5.67E008 25.4 -j4711.0Die jetzige Implementierung würde zwei komplexe Zahlen lesen. Eine Implementierung, die ein Zeilenende als Ende einer einer komplexen Zahl verwendet, liest zwei Werte und stellt dann einen Formatfehler fest, und eine Implementierung, die zusätzlich alleinstehende Imaginärteile akzeptiert, liest drei Werte ohne Fehler ein. Das will also gut überlegt sein.
Ich postuliere einfach, dass eine komplexe Zahl hinter dem Realteil mit jedem Zeichen oder EOF endet, was nicht '+' oder '-' ist, oder wenn ein Imaginärteil existiert eben hinter diesem endet. Alleinstehende Imaginärteile seien nicht zulässig, also ein Realteil muss immer vorhanden sein. Weiter darf eine komplexe Zahl durch keine White Space Character unterbrochen sein.
Das Unterbinden des Überspringens von White Space Character geschieht mit std::noskipws. Da ich für spätere Leseroutinen den Zustand des Streams nicht veränderrn möchte und ich auch nicht weiß, ob das skipws-Flag gesetzt ist oder nicht, merke man sich einfach den aktuellen Zustand und stelle ihn am Ende des Streaming-Operators wieder her. Eine elegante Lösung dazu bietet boost.io.ios_state an.friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value { const std::ios_base::fmtflags oldFlags = in.flags(); // merken wg. std::noskipws double real, imag; PlusMinus plus; if( in >> real >> std::noskipws >> plus >> imagChar >> imag ) cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag ); in.flags( oldFlags ); // alten Zustand des Streams wieder herstellen return in; }Wichtig: Das Starten des Einlesens des Realteils überlassen wir hier noch dem Aufrufer von ComplexReader. Natürlich dürfen vor dem Realteil White Space Character stehen und erst danach verhindert man das Überspringen derselben.
Ein Test:eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 +1.234-j5.67E008 gelesen: (1.234,-5.67e+008) +1.234- j5.67E008 -> Lesefehler, da ein Space hinter den Operator stehtZur Unterbringung der Funktionalität des optionalen Lesens des Imaginärteils gibt es im Prinzip zwei Möglichkeiten. Zum einen kann man das völlig unabhängig von dem was schon implementiert ist, einbringen. Also in der Art
if( in >> real >> std::noskipws ) { if( kommt_noch_was( in ) ) // <== kommt noch der Imaginärteil? { if( in >> plus >> imagChar >> imag ) cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag ); } else cr.m_target = std::complex< double >( real, 0.0 ); }Vorteil: alles was bisher existiert, wird nicht angefasst, und funktioniert sicher wie vorher. Der Nachteil ist, dass 'kommt_noch_was()' wissen muss, was der Operator tut, nämlich '+' oder '-' lesen. Damit schafft man hier eine Redundanz zwischen der Implementierung des Operators und der Funktion 'kommt_noch_was()' und auch mit der Streaming-Operator des ComplexReaders, da es von der Reihenfolge der Teile abhängt, ob auf +/- oder vielleicht doch auf 'j' geprüft wird.
Es wäre leichter gewesen, wenn man hier die Negativprüfung postuliert hätte. Z.B. eine komplexe Zahl endet mit einem White Space oder EOF. Dann könnte 'kommt_noch_was()' eben dies abfragen. Das ist in der Praxis aber unschön, man stelle sich vor, die komplexe Zahl ist Teil einer Matrix und unmittelbar hinter ihr stünde ein ')' oder ein ';'. Das würde einen Lesefehler ergeben, der nicht erwünscht ist.
Die zweite Möglichkeit besteht darin, das optionale Lesen mit in den Operator PlusMinus zu integrieren. Falls in der Lesefunktion von PlusMinus kein '+' oder '-' gefunden wird, so stelle man das Zeichen zurück und merke sich dies.
// -- Lektion 5 #include <iostream> #include <complex> template< typename T > struct ComplexReader { struct PlusMinus { friend std::istream& operator>>( std::istream& in, PlusMinus& pm ) { if( in >> pm.m_op ) { pm.m_valid = (pm.m_op == '+' || pm.m_op == '-'); if( !pm.m_valid ) // falls kein Operator gelesen wurde, Zeichen zurückstellen in.putback( pm.m_op ); } return in; } bool positive() const { return m_op == '+'; } bool ok() const { return m_valid; } private: char m_op; bool m_valid; // true, falls m_op=='+' oder m_op=='-' }; friend std::istream& imagChar( std::istream& in ) { char c; if( in >> c && c != 'j' ) // <== hier steht das 'j' in.setstate( std::ios_base::failbit ); return in; } typedef T value_type; ComplexReader( std::complex< value_type >& target ) : m_target( target ) {} friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value { const std::ios_base::fmtflags oldFlags = in.flags(); // merken wg. std::noskipws double real, imag; PlusMinus plus; if( in >> real >> std::noskipws >> plus ) { if( plus.ok() ) // <== kommt noch der Imaginärteil? { if( in >> imagChar >> imag ) cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag ); } else cr.m_target = std::complex< double >( real, 0.0 ); } in.flags( oldFlags ); // alten Zustand des Streams wieder herstellen return in; } private: std::complex< value_type >& m_target; }; template< typename T > ComplexReader< T > read_complex( std::complex< T >& z ) { return ComplexReader< T >( z ); } int main() { using namespace std; cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl; complex< double > z; while( cin >> read_complex( z ) ) cout << "gelesen: " << z << endl; return 0; }Ein abschließender Test:
eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 -1.234-j5.6E007 gelesen: (-1.234,-5.6e+007) -1.234 gelesen: (-1.234,0) 56.1 5.001+j4 gelesen: (56.1,0) gelesen: (5.001,4) 42 gelesen: (42,0) +j7.1 -> Lesefehler; alleinstehender Imaginärteil ist nicht vorgesehenund das soll für heute reichen.
Gruß
Werner
-
Leider funktioniert die Funktion immer noch nicht. So langsam bin ich wirklich am verzweifeln, da aus einer so einfachen Sache eine so komplizierte Sache gemacht wurde.
Wie ich persönlich als "Nicht-Programmierer" daran gehen würde ist einfach:
Zeile einlesen mit getline(f,s) und in einen string kopieren. Nun die Länge des strings kontrollieren und dadurch dann Fallunterscheidung ob die Zahl komplex oder reel ist. Wenn die Zahl reel ist dann zerstückel ich den string so, dass er wieder ne Zahl ergibt und FERTIG!!! Sieht nicht schön aus und wird auch wohl kein Mensch auf den ersten Blick verstehen, aber es ist einfach, simpel und funktioniert. Und das schaff ich als "Nicht-Programmierer" mit den einfachsten Funktionen. Ich dachte Ihr könnt mir mit eurem Wissen vllt. paar Tipps geben, wie das Programm kompakter wird und einfacher zu programmieren, aber ich merke nur, dass das Programm nach jedem "Tipp" kaputter wird, da wieder was anderes nicht funktioniert...Für jeden der es sich nochmal gerne ansehen möchte, dass ist die Funktion die nicht von mir geschrieben sondern von den verschiedensten Forenmitgliedern "gebastelt" wurde. Falls jmd. noch einen Tipp für mich hat, wäre ich sehr dankbar, aber ich denke wohl kaum das das noch was wird...
vector<complex<double> > readComplexNumbers(const char* filename,const char* tag, const char* attributeName){ vector<complex<double> > complexNumbers(30750); ifstream f(filename); //solange Zeilen ignorieren, bis der Tag gefunden wurde while(!isAttributeTag(f,tag,attributeName)){ f.ignore( numeric_limits< streamsize >::max(), '\n' ); } //die Zeile des Tags ebenso ignorieren f.ignore( numeric_limits< streamsize >::max(), '\n' ); //nun Werte lesen double real, imag; char plus, j, var; int a = 0; int b = 0; for(int p=0; p<30750; p++){ f>>real; f.ignore( 1 ); // überliest ein Zeichen, aber es wird im Streambuf berücksichtigt. var = f.peek(); if(var == 'j'){ f.unget(); // Du kannst Dich nicht darauf verlassen, dass dies immer geht ! f>>plus>>j>>imag; if(plus == '-'){ imag *= -1; } } else{ imag = 0; f.unget(); } if(f.fail()){ // auf Fehler prüfen break;} complexNumbers[p]=complex<double>(real,imag); cout<<complex<double>(real,imag)<<endl; } return complexNumbers; }
-
Javun schrieb:
Wie ich persönlich als "Nicht-Programmierer" daran gehen würde ist einfach:
Zeile einlesen mit getline(f,s) und in einen string kopieren.Es gibt ein getline, das gleich in einen string einliest.
Javun schrieb:
Nun die Länge des strings kontrollieren und dadurch dann Fallunterscheidung ob die Zahl komplex oder reel ist.
Javun schrieb:
Wenn die Zahl reel ist dann zerstückel ich den string so, dass er wieder ne Zahl ergibt und FERTIG!!! Sieht nicht schön aus und wird auch wohl kein Mensch auf den ersten Blick verstehen, aber es ist einfach, simpel und funktioniert. Und das schaff ich als "Nicht-Programmierer" mit den einfachsten Funktionen.
Dann den string in einen stringstream geben und jenen dann wie gewohnt auslesen.
Das halte ich zunächst für eine sehr gute Idee.
-
War doch gar nicht so schwer?!
Soweit ich es kontrollieren konnte funktioniert die Funktion jetzt. Vllt. findet ja aber noch jmd. einen Fehler?
Und noch eine Frage am Rande:
Wenn ich eine double Zahl habe, wie z.B. 1.47086e-09, wie kann ich daraus ein Excel/OpenOffice kompatibles Format bekommen? Also als erstes müsste statt . ein Komma und komischer Weise macht Excel aus e-09 E-04...Also aus der Zahl 1.47086e-09 macht Excel 1,47E-4...Auch hier könnte ich natürlich wieder die Zahl als string zerlegen usw., aber vllt. gibt es ja auch eine einfacherere Möglichkeit...
//nun Werte lesen string s; double real, imag; char plus, j; for(int p=0; p<21525; p++){ getline(f, s); stringstream ss(s); if(s.size() > 22){ ss>>real>>plus>>j>>imag; if(plus == '-'){ imag *= -1; } } else{ ss>>real; imag=0; } complexNumbers[p]=complex<double>(real,imag); }
-
Die Unterscheidung anhand der magischen Zahl 22 ist total willkürlich und wird dir oder jemand anderem in Zukunft noch Probleme machen. Warum unterscheidest du nicht anhand eines eindeutigen Kriteriums, z.B. ob ein 'j' drin vorkommt oder nicht?
-
SeppJ schrieb:
Die Unterscheidung anhand der magischen Zahl 22 ist total willkürlich und wird dir oder jemand anderem in Zukunft noch Probleme machen. Warum unterscheidest du nicht anhand eines eindeutigen Kriteriums, z.B. ob ein 'j' drin vorkommt oder nicht?
Oder ob nach
ss>>real>>plus>>j>>imag;
in j ein 'j' steht, anderenfalls plus, j und imag nicht verwendet werden.
-
SeppJ schrieb:
Die Unterscheidung anhand der magischen Zahl 22 ist total willkürlich und wird dir oder jemand anderem in Zukunft noch Probleme machen. Warum unterscheidest du nicht anhand eines eindeutigen Kriteriums, z.B. ob ein 'j' drin vorkommt oder nicht?
Also ich versteh schon worauf Du hinaus willst, aber das Programm welches ich schreibe ist ein reines Auswertunsprogramm für das Programm Qucs, da dieses leider keine Export Funktion bietet.
Die Zahlen haben daher immer den gleichen Aufbau bzw. die gleiche Länge. Hier Mal einen Ausschnitt als Beispiel:
+2.94796701276e-013+j1.10171292085e-012 +0.00000000000e+000 +0.00000000000e+000+j2.52435489671e-017 +8.45882878540e-014+j3.16122973146e-013Da die Zahl nicht größer sein kann als 22 wenn sie reel ist und größer als 22 sein muss wenn sie komplex ist, ist das doch ein eindeutiges Kriterium oder etwa nicht?
Oder ob nach
ss>>real>>plus>>j>>imag;
in j ein 'j' steht, anderenfalls plus, j und imag nicht verwendet werden.Das müsste natürlich auch funktionieren.
-
Kannst du das 'j' nicht weglassen und prüfen, ob das Zeichen (eigentlich 'j') ein '\n' (Zeilenumbruch) ist oder nicht ?
-
Hacker schrieb:
Kannst du das 'j' nicht weglassen und prüfen, ob das Zeichen (eigentlich 'j') ein '\n' (Zeilenumbruch) ist oder nicht ?
Theoretisch gesehen bestimmt, aber wie sieht das praktisch aus?!
Wenn ich stringstream ss>>double xyz mache, dann ist die Zahl ja weg. Also müsste ich jetzt das aktuelle oder das nächste Zeichen überprüfen? Und wie?
Funktionieren tut es ja bereits, ist nur eine Frage aus Interesse...Ich denke auch da wäre der Vorschlag von volkard besser.
-
Javun schrieb:
Funktionieren tut es ja bereits, ist nur eine Frage aus Interesse...Ich denke auch da wäre der Vorschlag von volkard besser.
Ich fürchte, mein Vorschlag ist viel schlechter.
Weil er nur nach "irgend einem" Fehler prüft.
1.245345353e07-j1.5fehler456
würde er als korrekte reelle Zahl 1.24e7 lesen.
Eigentlich sollte man auch versuchen, daß wenn man schon so ein genau gegebenes Format hat, möglichst viele Formatfehler aufgedeckt werden.
Also eigentlich entweder vorher die ganze Zeile nach dem j scannen, oder die Zeile Stückchenweise lesen und ein paar mehr ifs benutzen.
-
Werner Salomon: Wieder einmal ein sehr guter Beitrag von dir, der zu schade ist, um in der Versenkung zu verschwinden. Was hälst du davon, deine Lektionen als Artikel zu veröffentlichen? Wenn du willst, kann ich dir die Arbeit auch abnehmen.
-
Werner Salomon:
Mir kam da eine komische Idee. Also klar, erst eine ganze Zeile schnell zu lesen, sie in einen string zu kopieren, den in einen stringstream zu kopieren und dann nochmal zu lesen, das hat unnötige Kopierkosten.
Aber es ist oft so einfach...
Anfänger und ich mögen das.Könnte man statt
for(int p=0; p<21525; p++){ getline(f, s); istringstream ss(s); //parsen von ss }sowas wie
for(int p=0; p<21525; p++){ igetline_proxy ss(f); //parsen von ss, ganz unverändert }machen, wobei ss keine Kopie der Daten haben muß, sondern nur weiterleitet auf den ehrlichen streambuffer des files?
este Idee: Der Destruktor des igetline_proxy muß den eigentlichen istream noch lesen, bis er gegen eof oder einen Zeilenumbruch läuft, damit es sich so anfühlt, als hätte man vorher die ganze Zeile gelesen.
-
Oder man nimmt einfach meine Lösung. Die ist super kurz. Und noch viel superer einfach!

-
314159265358979 schrieb:
Oder man nimmt einfach meine Lösung. Die ist super kurz. Und noch viel superer einfach!

Ein Lob an die transzendente Zahl.
-
314159265358979 schrieb:
Oder man nimmt einfach meine Lösung. Die ist super kurz. Und noch viel superer einfach!

Welche genau?
-
314159265358979 schrieb:
template <typename Iter> std::complex<double> parse_complex(Iter begin, Iter end) { using namespace boost::spirit::qi; using boost::phoenix::ref; double real, imag = 0; char sign; if(!phrase_parse(begin, end, double_[ref(real) = _1] >> -((char_('+') | char_('-'))[ref(sign) = _1] >> 'j' >> double_[ref(imag) = _1]), ascii::space) || begin != end) throw parse_error("parse error here: " + std::string(begin, end)); return std::complex<double>(real, sign == '-' ? -imag : imag); }Um eine Lösungsmöglichkeit zu posten.
Die hier.

-
Fehlt nur noch ein "Ich bin es".
-
314159265358979 schrieb:
...
Ach, nö.