Operator >> überladen
-
Werner Salomon schrieb:
314159265358979 schrieb:
Warum ist getline doof?
Nehmen wir ein kleines Beispiel. Ich habe eine Konfigurationsdatei, die von meinem Programm eingelesen werden soll. Damit der Benutzer die Datei lesen und bearbeiten kann, werden die Daten zeilenweise in der Form "key = value" gespeichert. Einzelne Zeilen werden mit getline eingelesen und danach geparst.
wenn Du dies tust, so ignorierst Du den Parser, den Dir C++ zur Verfügung stellt - nämlich den std::istream - liest an ihm vorbei und bastelst Dir anschließend einen eigenen Parser. Dieses Vorgehen ist doch doof, oder?
Es gibt hier im Forum zu häuf Beispiele dafür. Die Ursache ist i.A. die Unkenntnis über die Fähigkeiten von istream.
Gruß
WernerIch persönlich würde darauf ein getline mit '=' als demin und anschließend ein getline mit '\n' als delim loslassen. Dann hat man mit minimalem Aufwand Key und Value in nem String. Oder gibts da eine bessere Vorgehensweise?
-
Ethon_ schrieb:
Ich persönlich würde darauf ein getline mit '=' als demin und anschließend ein getline mit '\n' als delim loslassen. Dann hat man mit minimalem Aufwand Key und Value in nem String. Oder gibts da eine bessere Vorgehensweise?
Das wäre doof, da man dann den ganzen Whitespace mitnimmt. Besser
stream >> key >> gleich >> value;
-
SeppJ schrieb:
Ethon_ schrieb:
Ich persönlich würde darauf ein getline mit '=' als demin und anschließend ein getline mit '\n' als delim loslassen. Dann hat man mit minimalem Aufwand Key und Value in nem String. Oder gibts da eine bessere Vorgehensweise?
Das wäre doof, da man dann den ganzen Whitespace mitnimmt. Besser
stream >> key >> gleich >> value;
Was aber erfordert dass sowohl Key als auch Value keine Whitespace enthalten. Da doch lieber einfach danach trimmen.
Obwohl meine Lieblingslösung ja wäre, die ganze Datei in einen vector zu laden, beim Parsen Null-Terminatoren einzufügen und einfach nur Pointer auf die Strings herumzureichen. Dann ist es nur eine Allocation.
-
Werner Salomon schrieb:
wenn Du dies tust, so ignorierst Du den Parser, den Dir C++ zur Verfügung stellt - nämlich den std::istream - liest an ihm vorbei und bastelst Dir anschließend einen eigenen Parser. Dieses Vorgehen ist doch doof, oder?
Der istream würde folgende (fehlerhafte) Datei als korrekt erkennen und einlesen.
foo = bar
Um das zu vermeiden lese ich zeilenweise und parse selbst. Es sei denn, man kann dem istream irgendwie sagen, \r und \n nicht zu überlesen, aber das wäre mir neu.
-
314159265358979 schrieb:
Um das zu vermeiden lese ich zeilenweise und parse selbst. Es sei denn, man kann dem istream irgendwie sagen, \r und \n nicht zu überlesen, aber das wäre mir neu.
Nun, das geht schon. Nötig dafür ist bloß ein bisschen obskures Wissen um Facets und Locales.
Aber ich würde an sich die obige Datei nicht unbedingt als falsch ansehen. Zumindest wäre es ein doofes Format, dass sich einerseits um Paare
key = wert
dreht, andererseits jedoch strikt zeilenbasiert ist.Falls man dieses Format jedoch vorgegeben hat, dann ist das mit dem getline durchaus eine gute Möglichkeit, wenn man sich nicht gut mit den Streams auskennt (und Werner ist einer der wenigen mir bekannten Personen, die sich gut genug auskennen, für die oben angedeutete Methode)
-
Gut, dieses obskure Wissen habe ich (leider) nicht. Ihr habt nicht zufällig eine Buchempfehlung?
Das Format hab ich mir gestern spontan ausgedacht. Was ist daran doof? Ich bezweifle mal, dass ein User, der meinem Programm so eine Datei vorlegt, das so wollte. Meiner Meinung nach wäre es schlimm, so eine Datei zu schlucken, da so Resultate auftreten könnten, die der User ganz und gar nicht beabsichtigt hätte.
-
Buchempfehlungen gab es mal von Werner (die Bücher selber habe ich nicht und daher auch nicht das Wissen):
Standard C++ IOStreams and Locales | ISBN: 0201183951C++ IOStreams Handbook | ISBN: 0201596415
Wie du siehst, die sind nicht gut zu bekommen, sonst hätte ich sie selber. Und sie sind uralt.
(Ich hoffe das waren die richtigen, die Buchempfehlung ist aus dem Gedächtnis zitiert)An dem Format stört mich, dass man einerseits Formatierungszeichen (das '=') hat, die schon die logische Struktur eindeutig machen, andererseits aber auch der Whitespace eine Bedeutung hat. Whitespace sehe ich in erster Linie als Formatierung für einen Menschen zum besseren Lesen an. Außer in Sprachen wie Python, aber da gibt es wiederum keine Sonderzeichen und es ist der Whitespace allein, der die logische Struktur ergibt. Ich persönlich hasse Formate, bei denen das gemischt ist (z.B. FORTRAN im alten Stil)
-
Was dich an dem = stört, verstehe ich nicht. Irgendeinen Trenner muss man eben einfügen, um Key und Value zu trennen. Und wo haben Whitespaces eine Bedeutung bei mir? (Oder zählst du Zeilenumbrüche zu den Whitespaces?)
Ich finde es immer noch seltsam, eine vom User in mehrere Zeilen getrennte Eigenschaft als gewollt anzunehmen. Niemand würde das so schreiben.
-
314159265358979 schrieb:
Was dich an dem = stört, verstehe ich nicht.
Das stört mich nicht. Das finde ich sogar gut. Es passt nur nicht zusammen, das man sowohl Sonderzeichen als auch die Formatierung heranzieht, um die logische Struktur zu definieren. Zumal doch durch das Gleichheitszeichen schon alles klar ist.
Und wo haben Whitespaces eine Bedeutung bei mir? (Oder zählst du Zeilenumbrüche zu den Whitespaces?)
Natürlich! Wer auf der Welt zählt denn Zeilenumbrüche nicht als Whitespace?
Ich finde es immer noch seltsam, eine vom User in mehrere Zeilen getrennte Eigenschaft als gewollt anzunehmen. Niemand würde das so schreiben.
Hätten K&R damals gesagt, dass niemand jemals Anweisungen auf mehrere Zeilen verteilen würde, würdest du dich heute ganz schön ärgern.
-
SeppJ schrieb:
Zumal doch durch das Gleichheitszeichen schon alles klar ist.
Jein. Natürlich ist es dadurch eindeutig. Aber ein einigemaßen brauchbares Format sollte zumindest versuchen, simple Fehler zu verhindern.
SeppJ schrieb:
Hätten K&R damals gesagt, dass niemand jemals Anweisungen auf mehrere Zeilen verteilen würde, würdest du dich heute ganz schön ärgern.
Naja. Sobald man Arrays oder sowas einführt, wird man wohl erlauben müssen, in mehrere Zeilen aufzuteilen. Allerdings würde ich dann nach jedem Eintrag ein Semikolon fordern oder so.
-
Dann änder das Format in "Key = Value;" und SeppJ ist zufrieden, ohne dass du eine einzige zusätzliche Zeile Code schreiben musst.
-
Ethon schrieb:
Dann änder das Format in "Key = Value;" und SeppJ ist zufrieden, ohne dass du eine einzige zusätzliche Zeile Code schreiben musst.
-
Hm. Klingt gar nicht mal so blöd.
while(is >> key >> Char<'='> >> value >> Char<';'>) ...
-
314159265358979 schrieb:
Char<'='>
Wasn das?
-
Hacker schrieb:
Wasn das?
Ich vermute mal aus dem Zusammenhang etwas wie:
template <char C> struct Char { friend istream& operator>>(istream & in, Char) { char c = C + 1; in >> c; if (c != C) in.setstate(ios::failbit); return in; } };
-
SeppJ schrieb:
Hacker schrieb:
Wasn das?
Ich vermute mal aus dem Zusammenhang etwas wie:
template <char C> struct Char { friend istream& operator>>(istream & in, Char) { char c = C + 1; in >> c; if (c != C) in.setstate(ios::failbit); return in; } };
Wieso
C + 1
?
-
Hacker schrieb:
Wieso
C + 1
?Damit c auf jeden Fall ungleich C ist.
Außerdem ist natürlich die Operatorüberladung als friend bei einem memberlosen struct etwas sinnlos, das war
Gewohnheiteine ganz clevere Optimierung der Compilezeit, damit mit weniger mögliche Überladungen im globalen Namensraum liegen.
-
SeppJ schrieb:
Hacker schrieb:
Wieso
C + 1
?Damit c auf jeden Fall ungleich C ist.
Da hat mein Gehirn mal wieder kurz eine Auszeit genommen. Natürlich, jetzt ergibt das viel Sinn
-
SeppJ schrieb:
Damit c auf jeden Fall ungleich C ist.
Dabei kann ein Überlauf auftreten, was zu implementation-defined behavior führt. Desweiteren habe ich zum Verstehen des Codes nun eine Minute länger gebraucht.
Warum diesen "Hack" verwenden, wenn du auch einfach if(is >> c && c != C) schreiben könntest? Premature und so :p
Übrigens wäre Char ein Funktionstemplate, kein Klassentemplate.
-
314159265358979 schrieb:
SeppJ schrieb:
Damit c auf jeden Fall ungleich C ist.
Dabei kann ein Überlauf auftreten, was zu implementation-defined behavior führt. Desweiteren habe ich zum Verstehen des Codes nun eine Minute länger gebraucht.
Warum diesen "Hack" verwenden, wenn du auch einfach if(is >> c && c != C) schreiben könntest? Premature und so :p
Übrigens wäre Char ein Funktionstemplate, kein Klassentemplate.
Das ist kein Hack, stinknormale Vorgehensweise, wenn du einen Integer möchtest, der garantiert ungleich einem Anderen ist.
Wird sehr oft bei Prüfsummen etc eingesetzt, um garantiert falsche Prüfsummen zu erzeugen.