Forum: PC-Programmierung eof in fstream


von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

ich will mit einer Schleife eine Datei mit fstream einlesen.
In dem Testfile steht nur der Satz "Das ist das Testfile."

Dabei gehe ich die Schleife einmal zu viel durch. Warum?


./a.out
Das0 ist1 das2 Testfile.3 Testfile.4

1
#include <iostream>
2
#include <string>
3
#include <fstream>
4
using namespace std;
5
6
class infstream {
7
   fstream filestr;
8
   string s;
9
   public:
10
     infstream(char* fname);
11
     void print();
12
     ~infstream();
13
};
14
15
infstream::infstream( char* fname) {
16
  filestr.open (fname, fstream::in );
17
   int i=0;
18
 // while (! filestr.eof())
19
   while (filestr)
20
   {
21
     filestr >> s;
22
     cout << s  << i++ <<' ';
23
   }
24
   cout << endl;
25
}
26
27
void infstream::print() {
28
  cout <<" this is my atext\n";
29
}
30
31
32
infstream::~infstream() {
33
  filestr.close();
34
}
35
36
int main( int argc, char* argv[])
37
38
{
39
40
char filename[]="test.txt";
41
42
43
  infstream  datei_in(filename);
44
 // datei_in.print();
45
46
47
48
  return 0;
49
}

: Verschoben durch User
von Karl H. (kbuchegg)


Lesenswert?

René D. schrieb:
> ich will mit einer Schleife eine Datei mit fstream einlesen.
> In dem Testfile steht nur der Satz "Das ist das Testfile."
>
> Dabei gehe ich die Schleife einmal zu viel durch. Warum?

Weil C++ nicht versucht in die Zukunft zu sehen.

EOF wird erst dann wahr, wenn ein Leseversuch gescheitert ist. D.h. du 
musst mit dem Ergebnis des Leseversuchs die Schleife steuern, bzw. den 
Stream nach dem Leseversuch befragen, ob er noch ok ist. Alle 
Stream-Lese-Operationen sind so ausgelegt, dass man sie immer dafür 
benutzen kann.
1
   while (filestr >> s)
2
   {
3
     cout << s  << i++ <<' ';
4
   }
5
   cout << endl;

von Karl H. (kbuchegg)


Lesenswert?

PS:

Das hier
1
   while (! filestr.eof())
2
   {
3
     filestr >> s;
4
     cout << s  << i++ <<' ';
5
   }

wäre die PASCAL-Variante. Funktioniert aber aus dem gleichen Grund nicht 
in C++, weil eben C++ eine Datei erst dann als vollständig abgearbeitet 
ansieht, wenn es nicht mehr daraus lesen kann.

eof() ist in C++ eine Funktion, die man hinterher bemüht um 
festzustellen, warum die Leseschleife abgebrochen wurde (was, wie du ja 
jetzt weißt, identisch ist mit: es konnte nicht mehr gelesen werden). 
Wurde die Schleife wegen eof abgebrochen, dann ist alles in Ordnung. 
Wenn nicht, dann ist irgendetwas anderes passiert. Zb hat jemand die CD 
aus dem Laufwerk genommen, die Netzwerkverbindung ist abgerissen, das 
Bluetoothmodul hat den Funkkontakt verloren, Lesefehler auf der 
Festplatte, der stream ist auf die Konsole geroutet und der Benutzer hat 
Ctrl-Z eingetippt, etc. etc. Es gibt viele Möglichkeiten warum eine 
Leseoperation schief gehen kann. EOF ist nur eine Möglichkeit davon.

1
   while (filestr >> s)
2
   {
3
     cout << s  << i++ <<' ';
4
   }
5
6
   if (!filestr.eof())
7
     cout << "ein unbekannter Fehler passierte beim Lesen der Datei\n";


Und nicht vergessen.
In C++ gibt es nur das allgemeine Konzept eines Streams. Ein Stream kann 
aber vieles sein. Der kann mit einem File verknüpft sein, der kann per 
Netzwerk auf ein File auf einem Server führen, der kann aber auch von 
der Eingabekonsole lesen. Woher soll denn ein Stream wissen, ob ein 
Benutzer noch was tippen will? Auf manchen Systemen ist es sogar 
möglich, dass ein anderer Prozess etwas in die Datei schreibt, während 
du sie liest. Durch Input Redirection auf der Shell ist es auch möglich, 
dass das was du als File ansiehst in Wirklichkeit ganz was anderes ist, 
etc. etc.

Daher dieses plakative, leicht merkbare "C++ versucht nicht in die 
Zukunft zu sehen". Erst beim Versuch aus dem Stream zu lesen entscheidet 
sich, ob das geht oder nicht und man kann sich hinterher ansehen, ob die 
Operation geklappt hat oder nicht und wenn nicht - warum nicht.

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

Danke Karl Heinz für deine ausführliche Erläuterung. Das interessanteste 
ist, dass filestr zwei Sachen hat. Es wirft etwas in den String und hat 
auch einen Rückgabewert, den ich als Indikator für die While Schleife 
nutzen kann.


Der Stream wird sich in meinem Fall auf Fileoperationen beschränken.
Ich will mir einen Parser und/oder einen Beautifier schreiben.

von Karl H. (kbuchegg)


Lesenswert?

René D. schrieb:
> Danke Karl Heinz für deine ausführliche Erläuterung. Das interessanteste
> ist, dass filestr zwei Sachen hat. Es wirft etwas in den String und hat
> auch einen Rückgabewert, den ich als Indikator für die While Schleife
> nutzen kann.

:-)
Wenn du mal ganz genau dir die Member der Stream Klassen ansiehst, dann 
wirst du merken, dass diese einen operator bool bzw. einen operator ! 
besitzen. Genau die sind es, de es dir ermöglichen ein Stream Objekt in 
einer Abfrage zu benutzen.
Und da ein >> das stream Objekt als Returnwert hat, funktionieren dann 
so Dinge wie

  while( fstr >> i )

das ist im Grunde nichts anderes als

  while( fstr.operator>>(i).operator bool() )

(wenn du mir die nicht ganz korrekte Schreibweise der operator Aufrufe 
erlaubst)

> Der Stream wird sich in meinem Fall auf Fileoperationen beschränken.

Das hab ich schon so verstanden. C++ ist aber so aufgebaut, dass sich 
alles auf den kleinsten gemeinsamen Nenner stützt, welcher 'stream' 
heißt und nach Möglichkeit alles in dieses allgemeine Stream-Konzept 
eingebunden wird. Und daher ist diese Systematik so gestaltet, dass sich 
so gut wie alles darin abbilden lässt. Dies ist wiederrum wichtig, damit 
man die Dinge aus std::algorithm auch auf streams (ohne Ansehen des 
speziellen streams) anwenden kann.

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

Karl Heinz Buchegger schrieb:



> :-)
> Wenn du mal ganz genau dir die Member der Stream Klassen ansiehst, dann
> wirst du merken, dass diese einen operator bool bzw. einen operator !
 alles in dieses allgemeine Stream-Konzept
> eingebunden wird. Und daher ist diese Systematik so gestaltet, dass sich
> so gut wie alles darin abbilden lässt. Dies ist wiederrum wichtig, damit
> man die Dinge aus std::algorithm auch auf streams (ohne Ansehen des
> speziellen streams) anwenden kann.

Karl Heinz hast du noch einen Tipp,
wie man einen eigenen Manipulator schreibt?

Ich habe in meinen Büchern nur die Anwendung von vorhanden Manipulatoren 
gefunden.

Ich will mehrere Textfiles nacheinader parsen und Informationen aus den 
Headern sammeln. Es sind alles String Operationen, deshalb will ich auch 
kein Template nutzen.



ostream& find_entry(ostream &os)  { return cut(find(&os));

von Karl H. (kbuchegg)


Lesenswert?

René D. schrieb:


> Karl Heinz hast du noch einen Tipp,
> wie man einen eigenen Manipulator schreibt?

Nein, leider.
Hab ich noch nie gemacht.

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

Ich habe es noch nicht ausprobiert. Der Code geht in die richtige 
Richtung.
Das erste Beispiel ist auch ohne Template.

http://cplus.kompf.de/artikel/stream3.html

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

Die Stream könnten eine feine Sache sein. Man muss eben doch wieder ein 
paar Randbedingungen wissen, die nicht so leicht auffindbar sind.
1
#include <iostream>
2
3
using namespace std;
4
5
int main() {      
6
   
7
    cout << cin;
8
    return 0;
9
}

Ich will die Pipeline in Linux ausnutzen. Das File läuft schon.

./a.out |ls
ahb_sseg.vhd   a.out   bea.cc~  include.h     myexcept.h   outfile.cpp~ 
outfile.hpp~  out.txt     test_pipe.cpp


Nur wenn die Pipeline leer ist, kommt ein Hexwert.

./a.out
0x601050red@linux-nrd1:~

Auf was muss ich cin testen?

von Rolf M. (rmagnus)


Lesenswert?

René D. schrieb:
> Ich will die Pipeline in Linux ausnutzen. Das File läuft schon.

Nein.

> ./a.out |ls

Damit übergibst du die Ausgabe von a.out an ls, das sich dafür aber 
nicht interessiert. ls schreibt dann seine Ausgabe direkt in dein 
Terminal.

> Nur wenn die Pipeline leer ist, kommt ein Hexwert.

Der kommt immer, weil cin für die Operation in einen Zeiger konvertiert 
wird, dessen Wert dann ausgegeben wird. Im ersten Fall siehst du den nur 
nicht, weil er in die Pipe an ls geschoben und nicht ausgegeben wird. 
Versuch's mal mit
1
ls | ./a.out

 So wie du dir das vostellst, funktioniert das nicht. Das muß eher so 
aussehen:
1
#include <iostream>
2
3
using namespace std;
4
5
int main()
6
{
7
    char c;
8
    while (cin.get(c))
9
    {
10
        cout.put(c);
11
    }
12
   return 0;
13
}

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

Das Programm war so schon kurz. Eigentlich schon genial.
Es mir schon peinlich, dass es nicht läuft. Ich muss zur meiner 
Entschuldigung dazu sagen ich, ich habe mehr mit hardwarenahem C zu tun.

Man, jetzt muss ich den Flow noch kontrollieren.

Ich will die gelisteten Files (durch ls generiert) nacheinander öffnen 
und mit einem Parser eine Liste von dem Inhalt der Files erstellen.

Die Files werden immer mehr und von Hand alles zu synchronisieren wird 
immer aufwendiger. Deshalb will ich mir ein Tool schreiben.

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

Rolf Magnus schrieb:

>  So wie du dir das vostellst, funktioniert das nicht. Das muß eher so
> aussehen:
>
>
1
> #include <iostream>
2
> 
3
> using namespace std;
4
> 
5
> int main()
6
> {
7
>     char c;
8
>     while (cin.get(c))
9
>     {
10
>         cout.put(c);
11
>     }
12
>    return 0;
13
> }
14
>

Ich habe den Programm mal ausprobiert. der Output kommt nur es beendet 
sich nicht. Als würde es noch auf eine Eingabe warten.
Erst wenn ich von Hand was eingebe wird es beendet.

von Rolf M. (rmagnus)


Lesenswert?

René D. schrieb:

> Ich habe den Programm mal ausprobiert. der Output kommt nur es beendet
> sich nicht.

Tut es bei mir. Sowohl wenn ich die Ausgabe von ls per Pipe reinschicke, 
als auch wenn ich eine Datei reinschiebe. Die Schleife müßte bei einem 
EOF abbrechen. Wie hast du es denn genau probiert?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

René D. schrieb:
> Ich will die gelisteten Files (durch ls generiert) nacheinander öffnen
> und mit einem Parser eine Liste von dem Inhalt der Files erstellen

Wäre es dann nicht einfacher gleich die Files/Directories mit der 
passenden API zu lesen? (z.B.
http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm )

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

> Wäre es dann nicht einfacher gleich die Files/Directories mit der
> passenden API zu lesen? (z.B.
> http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm )


die Boost lib habe ich noch nicht durchschaut. Und die Fehlermeldungen 
sind im Fehlerfall immer so groß.

Ich habe gerade das Beispiel simple_ls.cpp mal compiliert. Lief so weit.


Normalerweise nutze ich Shell scripte, nun wollte ich die Shell scripte 
in den  C/C++ Code verlagern, Da ich hier sowie so was programmieren 
muss. Zusätzlich wollte ich es elegant manchen und hatte mir hier was 
erhofft, das so scheinbar nicht geht.

Werde mal weiter forschen und mich dann entscheiden.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

René D. schrieb:
> Zusätzlich wollte ich es elegant manchen und hatte mir
> hier was erhofft, das so scheinbar nicht geht
Es gibt ja noch viele andere (Skript)Sprachen, die so etwas schon können 
(Dateiverarbeitung etc...), C++ ist in der Hinsicht schon recht 
spartanisch.

René D. schrieb:
> die Boost lib habe ich noch nicht durchschaut. Und die
> Fehlermeldungen sind im Fehlerfall immer so groß.
Hat aber nix mit boost direkt zu tun, sondern ist eher C++ spzifisch. 
Und wenn man sich mit einer Sache nicht beschäftig, wird es auch nicht 
besser ;-)
Was eigenes mag erstmal einfach erscheinen, aber spätestens wenn jemand 
in seiner Konfig z.B. ein anderes ausgabeformat als default für 'ls' 
einstellt geht der Stress los...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.