Auslesen von komplexen Zahlen aus einer Datei
-
SeppJ schrieb:
Das ist ja nicht mehr auszuhalten, wie hier der Einäugige mit den Blinden über Farbe diskutiert. Ich erbarme mich heute Abend oder morgen mal. Nur damit wir uns recht verstehen:Du suchst eine Funktion, die aus einem vorgegebenen istream einen std::vector<complex> macht? Die Zahlen können dabei in zwei Formaten vorliegen
a) realteil (plus oder minus) j imaginärteil
b) realteil
und sind jeweils durch Zeilenumbruch getrennt. Soweit richtig? Und was markiert das Ende? Wenn etwas kommt, das nicht diesem Format entspricht?Das ist alles soweit korrekt.
Beendet wird das ganze durch "</indep>", das ist die Zeile nach der letzten Zahl. Es liegt jedoch auch die Anzahl der insgesamt auszulesenden Zahlen vor. Letztlich geht es daher nur um die for()-Schleife...
Was anderes kann nicht vorkommen, es liegt entweder eine komplexe oder eine reele Zahl vor!
-
Javun schrieb:
Beendet wird das ganze durch "</indep>", das ist die Zeile nach der letzten Zahl. Es liegt jedoch auch die Anzahl der insgesamt auszulesenden Zahlen vor.
Ahh, ok. Merkwürdiges Format, wo die Anzahl der werte drinsteht, aber das macht die Sache sogar noch einfacher.
Letztlich geht es daher nur um die for()-Schleife...
Bei diesem Monsterthread (und das ist ja schon der 2. zu diesem Thema) habe ich den Eindruck, dass fehlende Abstraktion das Problem ist. Alles hübsch in Unterprobleme eingeteilt würde alles viel einfacher und insgesamt kürzer machen. Ich erinnere mich noch mit Grauen an das Monstrum aus deinem vorherigen Thread.
Was anderes kann nicht vorkommen, es liegt entweder eine komplexe oder eine reele Zahl vor!
Ausgezeichnet. Habe gerade keine Zeit, aber morgen. Sollte schnell gehen und dürfte nicht lang werden.
-
Ersteinmal vielen Dank, dass Du Dir die Mühe machen möchtest. Aber ich würde auch schon gern erfahren, wo denn mein Fehler genau liegt.
Im Großen und Ganzen funktioniert ja soweit alles. Einziges Problem ist momentan die Unterscheidung von reelen und komplexen Zahlen. Ich habe es versucht mit den Tipps der anderen User hier zu lösen, jedoch bin ich hier zu keinem "richtigen" Ergebnis gekommen.
Für einen geübten Programmierer ist der Fehler bestimmt in weniger als einer Minute gefunden, deswegen würde ich mich sehr freuen, wenn mir jmd. evt. Heute noch einen Tipp geben könnte...
double real, imag; char plus, j, var; int a = 0; int b = 0; for(int p=0; p<7770; p++){ f>>real; // double Zahl wird erfolgreich gelesen f.seekg((ios::cur)+1); // der Zeiger wird +1 genommen var = f.peek(); // es wird das nächste Element gelesen if(var == 'j'){ // es wird kontrolliert ob das Element ein j war f.unget(); // nun soll zurückgesprungen werden an das Ende der ausgelesenen double Zahl f>>plus>>j>>imag; // nun lesen wir den Rest der Zeile aus, beim nächsten Durchlauf soll nun auch die nächste Zahl/Zeile gelesen werden, aber cur zeigt IMMER auf die gleiche Stelle if(plus == '-'){ imag *= -1; } } else{ imag = 0; f.unget(); } complexNumbers[p]=complex<double>(real,imag); }
-
Sag mal, das j kannst du genauso am Anfang und nicht mitten im Einzulesenen "String" platzieren.
Dann kannst du gleich zu Anfang sehen, ob du auch eine imaginäre Zahl einliest.Also sähen deine Zahlen so aus:
j-9.35124373769e-017-1.53956708493e-017
Probier mal sowas in der Art:
char j;// Wofür ist das ? int a = 0, b = 0;//Wofür auch immer die da sind ... for(int p=0; p<7770; p++) { double real, imag; char var= f.get(); f>>real; // double Zahl wird gelesen if(var == 'j') f>>imag; //Brauchst du hier überhaupt ein plus Char ? else imag = 0; complexNumbers.at(p)=complex<double>(real,imag); }
-
Hacker schrieb:
Sag mal, das j kannst du genauso am Anfang und nicht mitten im Einzulesenen "String" platzieren.
Dann kannst du gleich zu Anfang sehen, ob du auch eine imaginäre Zahl einliest.Also sähen deine Zahlen so aus:
j-9.35124373769e-017-1.53956708493e-017
Probier mal sowas in der Art:
char j;// Wofür ist das ? int a = 0, b = 0;//Wofür auch immer die da sind ... for(int p=0; p<7770; p++) { double real, imag; char var= f.get(); f>>real; // double Zahl wird gelesen if(var == 'j') f>>imag; //Brauchst du hier überhaupt ein plus Char ? else imag = 0; complexNumbers.at(p)=complex<double>(real,imag); }Leider auch hier immer das gleiche Ergebnis, es wird die gleiche Zeile immer und immer wieder ausgelesen...
-
Hey,
also hör mal auf SeppJ,
ansonsten solange:
A) Du kennst anfang und ende deiner Zahlenspalte
--- Dann kopier die doch in einen seperaten string
Du weißt das deine Spalte vieeeeele Zeilen hat die alle mit \n Enden?
--- Dann nutze Doch folgendes:std::string strTextWoAlleZahlenLiegen; std::vector<std::string> vecVieeeeeleZahlen; //... unsigned int zaehler=0; if(std::string::npos==zaehler=strTextWoAlleZahlenLiegen.find('\n')) { std::cout<<"ERROR"; } else vecVieeeeeleZahlen.push_back(strTextWoAlleZahlenLiegen.substr(zaehler));nun haste dann einen vector voller string's die jeweils eine Zahl beinhalten.
Was musste nun tun?
Die Funktionstd::string::findkennst Du ja mitlerweile, suche also nach einem j
(mit der annahme das das Format kontinuierlich gleich ist)
und schon weisst Du welche Zeile eine Imaginäre und welche Zeile nur einen Realteil enthält.Nun Kannst Du mit dieser Fallunterscheidung die jeweilige Zeile entsprechend Parsen...
sei gegrüßt
-
SeppJ hat völlig recht. Das kann man einfach nicht so weiter laufen lassen. Javun hat ja auch schon fest estellt, dass Ihr Euch hier im Kreis dreht.
Hallo Javun,
ich werde versuchen es Dir zu erklären. Schritt für Schritt und in mehreren Lektionen. Erstmal was grundsätzliches - versuche Dein Problem immer(!) in mehrere Teilprobleme zu zerlegen. Wenn Du also eine komplizierte Datei einlesen willst, so versuche zunächst die einzelnen Elemente - z.B. hier eine komplexe Zahl - für sich zu lesen. Lesen aus einer Datei heißt lesen aus einem std::ifstream und da dies ein std::istream ist, ist das Lesen wie von std::cin. Das macht keinen Unterschied für da Einlesen an sich.
Auch das kann man noch in kleinere Teile zerlegen. In Deinem Fall in vier: den Realteil, den Operator+/-, das 'j' und den Imaginärteil.Ich fange ganz einfach an, mit Dingen, die Du schon kennen solltest, wie ich hier gesehen habe:
// -- Lektion 1 #include <iostream> #include <complex> int main() { using namespace std; cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl; double real, imag; char plus, imagChar; while( cin >> real >> plus >> imagChar >> imag ) cout << "gelesen: " << complex< double >( real, plus == '+'? imag: -imag ) << endl; return 0; }einfach die vier genannten Dinge als double bzw. char einlesen und eine komplexe Zahl daraus machen. Ein Test ergibt:
eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 -9.351E-017-j1.539e-017 gelesen: (-9.351e-017,-1.539e-017) 0+j0 gelesen: (0,0) 0**2 gelesen: (0,-2)natürlich ist dem Programm das Zeichen vor dem Realteil egal und beim Operator interessiert nur ob da ein '+' kommt oder was anderes. Das ist noch suboptimal. Aber bevor ich darauf eingehe, kapsele ich diese Funktionalität und zwar mit dem Ziel, so etwas hinschreiben zu können:
complex< double > z; while( cin >> /*mal sehn*/( z ) ) cout << "gelesen: " << z << endl;weil später, da wo das benutzt wird, will ich mich nicht mehr mit dem detailliertem Format herumschlagen, sondern einfach nur eine komplexe Zahle einlesen, so wie ich ein double lese.
(gleich geht's weiter)
-
Vielen Dank für die ausführliche Erklärung

Soweit hatte ich das ja bereits, danach ist mir aufgefallen, dass sowohl reele als auch komplexe Zahlen vorkommen können. Nun geht es um diese Fallunterscheidung...
Ich hab es probiert, aber anscheind war ich da auf dem falschen Weg. Die Möglichkeit erstmal alle Zeilen als string auszulesen und dann zu verarbeiten ist mir bewusst und so sah es auch in der 1. Version des Programmes aus. Nur wurde mir dann von so vielen Usern geraten das auslesen und verarbeiten direkt zu machen, dass ich das nun so umgesetzt habe...
Problem ist nur das ich an dieser Stelle jetzt nicht weiterkomme...
-
Hallo Javun,
wie bereits angekündigt, suche ich eine Möglichkeit das Einlesen einer komplexen Zahl ähnlich aufzurufen, wie das z.B. das Lesen eines double. Der Trick besteht nun darin, dass man sich eine kleine Hilfsstruktur baut, die in in Ihrem Konstruktor eine komplexe Zahl aufnimmt - genauer deren Referenz - und das Einlesen implementiert.
Angenommen da ist eine
struct ComplexReaderund ich möchte ein Objekt vonComplexReadereinlesbar machen, so schreibe ich fürComplexReadereinen sogenannten Streaming-Operator.struct ComplexReader { friend std::istream& operator>>( std::istream in, ComplexReader& cr ) { // .. hier kann ich lesen was ich will return in; } };Dann bin ich in der Lage so was hinzu schreiben:
ComplexReader cr1; if( cin >> cr1 ) { // cr1 wurde ohne Fehler gelesenJetzt kann man so einem Objekt vor dem lesen natürlich noch eine Referenz mitgeben. Das ganze nochmal
struct ComplexReader { ComplexReader( std::complex<double>& z ) : m_target( z ) {} friend std::istream& operator>>( std::istream in, ComplexReader& cr ) { // .. hier kann ich lesen und das Ergebnis nach cr.m_target schreiben return in; } private: std::complex<double>& m_target; };Dann kann man schreiben:
complex<double> z; ComplexReader cr1( z ); if( cin >> cr1 ) { // cr1 wurde ohne Fehler gelesen; d.h. 'z' enthält jetzt den gelesen WertDas schreibt sich natürlich besser und übersichtlicher in einer Zeile:
complex<double> z; if( cin >> ComplexReader( z ); ) { // 'z' enthält jetzt den gelesen Wertund - Oh Schreck das übersetzt nicht. Zumindest nicht mit einem Standard konformen Compiler. Ich will gar nicht erklären warum - das führt uns vom Thema weg.
Die Lösung besteht darin, den Parametercrim Streaming-Operatorconstzu machen odercrper Value zu übergeben. Da das Objektcrbeim Einlesen gar nicht verändert wird, ist dies ohne weiteres möglich.Gleichzeitig mache ich aus ComplexReader noch ein Template, da std::complex ja auch ein Template ist. Lass Dich davon nicht abschrecken, denke einfach, das T wäre ein double oder float, dann bleibt alles wie gewohnt.
// -- Lektion 2 #include <iostream> #include <complex> template< typename T > struct ComplexReader { 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; char plus, imagChar; if( in >> real >> plus >> imagChar >> imag ) cr.m_target = std::complex< double >( real, plus == '+'? imag: -imag ); return in; } private: std::complex< value_type >& m_target; }; int main() { using namespace std; cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl; complex< double > z; while( cin >> ComplexReader< double >( z ) ) cout << "gelesen: " << z << endl; return 0; }an der Funktionalität hat sich gegenüber Lektion 1 nichts geändert, aber das Einlesen der komplexen Zahl geschieht nur noch in dem Streaming-Operator von ComplexReader. Und das ist die Baustelle für die nächsten Lektionen.
Bevor es weiter geht, beseitige ich noch eine Unschönheit:
ComplexReader< double >( z )ist ein redundanter Ausdruck. Es steht double davor und z ist (und muss) ein complex<double> (sein). Das kann der Compliler für uns erledigen; mit einer sogenannten Factory-Funktion.template< typename T > ComplexReader< T > read_complex( std::complex< T >& z ) { return ComplexReader< T >( z ); }und das Einlesen wird zu:
while( cin >> read_complex( z ) ).. und in der nächsten Lektion geht es wirklich um das Lesen.

-
Javun schrieb:
Soweit hatte ich das ja bereits, danach ist mir aufgefallen, dass sowohl reele als auch komplexe Zahlen vorkommen können. Nun geht es um diese Fallunterscheidung...
Geduld

-
Lies immer bis zum nächsten Plus alles in einen String und nutze dann eine weitere Funktion/Methode für die Fallunterscheidung.
Die könnte dann z.B. so aussehen(Pseudocode):
MyFunction(string complex_number)
{
REAL a = 0 ,b = 0;
std::string::iterator it = complex_number.begin();
IT LAUFEN LASSEN BIS j auftritt(am ende hört er automatisch auf)
Substring auslesen und umwandeln
if(j aufgetreten)
read zahlen und wandele sie uma = wert des ersten substrings
b = wert des zweiten. Wenn kein real oder imaginärteil vorhanden ist, haben a und b ja schon die gewünschten werte.
-
Hallo Javun,
die letzte Lektion hat uns beim Lesen nicht weiter gebracht. Aber es war notwendig, um das Problem wirklich zu isolieren. Manchmal ist das sogar wichtiger als eine guter Algorithmus.
Im diesem Schritt widme ich mich nur dem Operator. Die Lösung ist für die Praxis vielleicht übertrieben, aber ich möchte Dir zeigen, dass man wirklich jedes Problem für sich lösen kann und dann die einzelnen Lösungen gar nicht mehr so schwer zu finden sind. Frei nach dem Motto: "Teile und herrsche"Da hier eine simpler
charnicht mehr ausreicht, baue ich wieder eine Struktur - hierPlusMinus. Da diese nur innerhalb von ComplexReader benötigt wird, mache ich sie lokal.template< typename T > struct ComplexReader { struct PlusMinus { friend std::istream& operator>>( std::istream& in, PlusMinus& pm ) { return in >> pm.m_op; } bool positive() const { return m_op == '+'; } private: char m_op; };und das Einlesen ändere ich zu
PlusMinus plus; char imagChar; if( in >> real >> plus >> imagChar >> imag ) cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );wieder ist die Funktionalität gleich geblieben, aber ich habe das Einlesen des Operators isoliert. Als Operatoren sind nur die Zeichen '+' und '-' erlaubt. Und genau das kann ich hier jetzt überprüfen. Falls das nicht passt, so setze ich das Fehlerflag im std::istream.
Alles zusammen sieht dann so aus:
// -- Lektion 3 #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; }; 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; char imagChar; 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; }und ein kleiner Test zeigt
eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 -9.3124E-002+j1.5E+017 gelesen: (-0.093124,1.5e+017) 9-X5 // das j ist ihm immer noch egal gelesen: (9,-5) 9?j5 -> Lesefehler kein + und kein -Du meinst vielleicht, dass man das auch direkt im Streaming-Operator von ComplexReader machen kann, aber der Unterschied ist, dass
PlusMinusden Stream auf Fehler setzt, wenn das Format nicht passt, und anschließend nichts mehr gelesen wird. Das hat bei der Analyse des Fehlers und für ein mögliches wieder aufsetzen große Vorteile.In der nächsten Lektion kommt das 'j dran.
-
Warum müsst ihr das denn alles kompliziert machen?! Ich bin Dir wirklich dankbar, dass Du Dir die Mühe machst, aber da ist einfach so viel neues bei, da sitz ich ja noch Wochen dran, wenn ich mir jede Funktion und sowas wie struct, friend usw. beibringen muss.
Ich wollte doch einfach nur Wissen, wo MEIN Fehler liegt, d.h. MEINEN Quellcode korrigieren. Aber seitdem ich hier im Forum bin hab ich meinen Quellcode schon 5 Mal komplett neu geschrieben und ganz ehrlich besser ist er dadurch nicht geworden. Die Fehler sind fast die gleichen, nur mit dem unterschied das ich jetzt jede 10 Zeile nicht mehr verstehe und von daher das Programm nicht anpassen kann.
Also ich bitte ich um einen Tipp, Hilfestellung zu meinen Quellcode.
Was muss in der for()-Schleife stehen und warum? Das wäre wirklich super, wenn mir das jmd. sagen könnte...
Ich habe schon eine Lösung wie ich es machen würde, aber das wird wahrscheinlich 99,999999% der Personen hier im Forum nicht gefallen. Weil es die "Idioten"-Lösung ist. Aber anscheinend scheint das die einzig machbare zu sein, denn ich dreh mich hier die ganze Zeit im Kreis und komm der Lösung kein Stück näher.
-
Javun schrieb:
Warum müsst ihr das denn alles kompliziert machen?! Ich bin Dir wirklich dankbar, dass Du Dir die Mühe machst, aber da ist einfach so viel neues bei, da sitz ich ja noch Wochen dran, wenn ich mir jede Funktion und sowas wie struct, friend usw. beibringen muss.
Schade - und ich hatte mir wirklich Mühe gegeben, es eben nicht kompliziert zu beschreiben - eben Schritt für Schritt. Und wenn es so viel wird - aus Deiner Sicht kompliziert, dann liegt es daran, dass das Problem eben kompliziert ist. Dir ist sicher aufgefallen, dass hier niemand eine schnelle und kurze und funktionsfähige Lösung hin geklatscht hat.
.. aber wenn schon struct für Dich etwas neues ist, dann solltest Du von solchen Aufgaben, wie Du sie uns hier beschreibst die Finger lassen. Das ist dann noch eine (oder zwei) Nummer zu groß für Dich.
Javun schrieb:
Ich wollte doch einfach nur Wissen, wo MEIN Fehler liegt, d.h. MEINEN Quellcode korrigieren.
Du meinst diesen Code hier:
Javun schrieb:
double real, imag; char plus, j, var; int a = 0; int b = 0; for(int p=0; p<7770; p++){ f>>real; // double Zahl wird erfolgreich gelesen f.seekg((ios::cur)+1); // der Zeiger wird +1 genommen var = f.peek(); // es wird das nächste Element gelesen if(var == 'j'){ // es wird kontrolliert ob das Element ein j war f.unget(); // nun soll zurückgesprungen werden an das Ende der ausgelesenen double Zahl f>>plus>>j>>imag; // nun lesen wir den Rest der Zeile aus, beim nächsten Durchlauf soll nun auch die nächste Zahl/Zeile gelesen werden, aber cur zeigt IMMER auf die gleiche Stelle if(plus == '-'){ imag *= -1; } } else{ imag = 0; f.unget(); } complexNumbers[p]=complex<double>(real,imag); }Ok .. kommt gleich
-
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.

-
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.
-
Ich habe die Stellen mit ### markiert. Der Aufruf in Zeile 7 (oben) ist schon syntaktisch falsch. Die Korrektur findest Du in Zeile 2 (unten). Das ungetc in Zeile 6 geht immer schief, weil es nichts zu unget'en gibt. Du hast ja vorher seekg aufgerufen.
f>>real; // double Zahl wird erfolgreich gelesen f.seekg( 1, ios::cur ); // 1 ist der Offset, ab ios::cur-rent-Position // ### var = f.peek(); // es wird das nächste Element gelesen if(var == 'j'){ // es wird kontrolliert ob das Element ein j war f.unget(); // nun soll zurückgesprungen werden an das Ende der ausgelesenen double Zahl // ### ist hier immer falsch, weil vorker ein seekg aufgerufen wurde f>>plus>>j>>imag; // nun lesen wir den Rest der Zeile aus, beim nächsten Durchlauf soll nun auch die nächste Zahl/Zeile gelesen werden, aber cur zeigt IMMER auf die gleiche StelleNur zur Info. Eine Datei ist kein lineares Stück Speicher, in dem man mit einem Zeiger herum wandern kann. Und der Einsatz von seekg und unget erfordert einige Kenntnis von den Internas eines Streams - und das soll einfach sein?
Ich versuche mal was:
double real, imag; char plus, j, var; int a = 0; int b = 0; for(int p=0; p<7770; 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); }obiges funktioniert in dem Umfeld, wo Du es einsetzt; so weit ich es beurteilen kann. Mit EOF kommt die Konstruktion nicht klar. Ist aber wohl auch nicht notwendig, da in Deiner Datei hinter den komplexen Zahlen immer noch was kommt.
Probier's mal aus.
Gruß
Werner
-
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