 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
James Kanze Guest
|
Posted: Thu Nov 09, 2006 3:32 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
Stefan Reuther wrote:
| Quote: | Rolf Magnus wrote:
Stefan Reuther wrote:
Ernst Baumann wrote:
Was ist dann aber: extern _CRTIMP?
Compilermagie. Aller Wahrscheinlichkeit ist das ein Makro,
das zu etwas noch viel abgefahrenerem expandiert.[...] Der
Compiler muss nur sicherstellen, dass du nach '#include
iostream>' das Objekt 'std::cout' benutzen kannst.
"Nur" ist gut. Er muss insbesondere sicherstellen, dass
std::cout auch von globalen Objekten in deren Konstruktoren
schon benutzt werden kann. Es reicht also nicht, std::cout
zu einem normalen globalen Objekt zu machen, denn dann
könnte es sein, dass es schon verwendet wird, bevor es
korrekt initialisiert wurde. Deshalb braucht man hier eine
Sonderbehandlung. Und die wird vermutlich durch dieses Makro
ausgelöst.
|
Ich weiß nicht, woher er dieses hat? Nicht aus der Norm, in
jedem Fall. Dazu gilt es meistens nicht (oder galt nicht, in
jedem Fall -- ich habe seit lange nicht mehr angeschaut).
| Quote: | Dafür gibt's doch std::ios_base::Init, das aus einem mir unerfindlichen
Grund im Standard festgeschrieben ist.
|
Weil der Programmierer es benutzen muss. Alles, was garantiert
ist, ist, dass die Objekte nach Einrichten einer Instanz von
std::ios_base::Init auch eingerichtet und benutzbar sind. Wenn
du also cout bzw. cerr im Constructor eines statisch angelegten
Objektes benutzen willst, musst du auch eine Instanz von
std::ios_base::Init in der übersetzungseinheit wo das Objekt
definiert ist, vor die Definition des Objektes, definieren.
In den klassischen iostream wurde es auch garantiert, dass
<iostream.h> eine Instanz von ios::Init enthielt. Diese
Anforderung steht aber nicht in der Norm. Die meisten
Implementierungen aber tun es (in <iostream>). Aber das reicht
nicht immer aus. Machst du etwas wie:
Datei Appli.cc:
#include "MeineKlasse.hh"
MeineKlasse statischeInstanze ;
Datei MeineKlasse.cc:
#include "MeineKlasse.hh"
#include <iostream>
#include <ostream>
MeineKlasse::MeineKlasse()
{
std::cout << "Ich bin's" << std::endl ;
}
Dann musst du eine Instanz von std::ios_base::Init in Appli.cc
definieren; eine Instanz in MeineKlasse.cc hilft nicht. (Eine
Lösung wäre:
MeineKlasse::MeineKlasse()
{
static std::ios_base::Init
dummyForInitialization ;
std::cout << "Ich bin's" << std::endl ;
}
| Quote: | Ich denke eher, _CRTIMP expandiert zu sowas wie __declspec(dllimport),
was man unter Windows braucht, um Symbole aus einer DLL zu importieren
(unter Unixen braucht man nichts besonderes dazu).
|
Ich würde auch so etwas vermuten.
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Thu Nov 09, 2006 11:51 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
James Kanze wrote:
| Quote: | Stefan Reuther wrote:
Dafür gibt's doch std::ios_base::Init, das aus einem mir unerfindlichen
Grund im Standard festgeschrieben ist.
Weil der Programmierer es benutzen muss. Alles, was garantiert
ist, ist, dass die Objekte nach Einrichten einer Instanz von
std::ios_base::Init auch eingerichtet und benutzbar sind. Wenn
du also cout bzw. cerr im Constructor eines statisch angelegten
Objektes benutzen willst, musst du auch eine Instanz von
std::ios_base::Init in der übersetzungseinheit wo das Objekt
definiert ist, vor die Definition des Objektes, definieren.
In den klassischen iostream wurde es auch garantiert, dass
iostream.h> eine Instanz von ios::Init enthielt. Diese
Anforderung steht aber nicht in der Norm.
|
Gut, danke, das erklärt einiges. Genau diese Garantie hab ich nämlich
gesucht und nicht gefunden (nur eine Fußnote "265 Constructors and
destructors for static objects can access these objects to read input
from stdin or write output to stdout or stderr.").
Wenn die Implementation also kein Init-Objekt für mich erzeugt, ist auch
klar, warum ich das tun können muss.
Stefan |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Fri Nov 10, 2006 11:26 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
| Quote: |
Mit dem clear bzw. nicht clear hatte ich noch ein Problem.
Nachdem ich also in meinem Demoprogramm (das aus einer Datei liest)
"-3.5e" nicht in eine Zahl umwandeln konnte (und deshalb eofbit=1 und
failbit=1 wird), habe ich die Datei geschlossen (ohne vorher clear zu
machen) und dann nochmals geöffnet und _sofort_ (d.h. ohne vorher
irgendwelche anderen Aktionen zu machen) eofbit, failbit, badbit
abgefragt. Alle hatten den Wert 1. Warum? Wurden sie nicht durch das
Schließen der Datei automatisch zurückgesetzt?
Wenn das so wäre, müsste man nach dem Öffnen einer beliebigen Datei
zuerst mal clear machen, da die Datei ja in einem Fehlerzustand sein
könnte.
Nein, du musst nach dem close() clear() aufrufen, um die Fehlerzustände
wegzuräumen. Eine komplett neu erfolgreich eröffnete Datei
std::ifstream huhu("haha");
hat natürlich keine Fehlerzustände.
1)Wenn _während_ des Programmlaufs eine Datei in einen Fehlerzustand |
kommt, muß man clear() machen, bevor man sie mit close() schließt um
bei einem erneuten open() (immer noch im gleichen Programmlauf)
wieder einen Dateizugriff machen zu können.
2) Wenn allerdings das Programm beendet wurde (ohne vorher clear() zu
machen), wird die Datei automatisch (vermutlich durch das
Betriebssystem) geschlossen. Dadurch, dass _nach_ beendetem Programm
zwischen dem Programm und der Datei keine Beziehung mehr besteht,
befindet sich die Datei auch nicht mehr in eineem Fehlerzustand.
Wenn man jetzt nochmals ein Programm startet, das diese Datei öffnet,
ist diese Datei nach dem Öffen nicht mehr in einem Fehlerzustand (im
Gegensatz zu 1)).
Ist das richtig?`
| Quote: |
Ergebnis:
Es ist programmtechnisch nicht möglich, herauszubekommen (d.h. zu
unterscheiden), ob alle Daten einer Datei korrekt ausgelesen werden
konnten (d.h. ohne Formatfehler) oder ob beim Lesen des _letzten_
Bytes einer Datei ein Fehler (Formatfehler) aufgetreten ist.
Ist das richtig?
Ja, bei dieser Methode nicht.
Wie soll man es sonst machen?
Zeilenweise lesen und parsen.
Das verstehe ich nicht: |
Meinst du, dass man die ganze Datei Byte für Byte in ein Feld (array)
auslesen soll und danach dieses Feld analysieren und dann erst
gegebenenfalls auf die Datei zugreifen soll. D.h. wenn z.B. der
vollständige Inhalt der Datei -3.5e sei und man dann das Feld ausliest
und -3.5e bekommt, weiss man dass man diese Datei nicht in eine Zahl
auslesen kann und läßt es dann eben bleiben.
Hast du das gemeint, bzw. ist dieser Gedankengang richtig?
mfg
Ernst |
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Sun Nov 12, 2006 1:02 am Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
Ernst Baumann wrote:
| Quote: | Nein, du musst nach dem close() clear() aufrufen, um die Fehlerzustände
wegzuräumen. Eine komplett neu erfolgreich eröffnete Datei
std::ifstream huhu("haha");
hat natürlich keine Fehlerzustände.
1)Wenn _während_ des Programmlaufs eine Datei in einen Fehlerzustand
kommt, muß man clear() machen, bevor man sie mit close() schließt um
bei einem erneuten open() (immer noch im gleichen Programmlauf)
wieder einen Dateizugriff machen zu können.
|
Ja.
| Quote: | 2) Wenn allerdings das Programm beendet wurde (ohne vorher clear() zu
machen), wird die Datei automatisch (vermutlich durch das
Betriebssystem) geschlossen. Dadurch, dass _nach_ beendetem Programm
zwischen dem Programm und der Datei keine Beziehung mehr besteht,
befindet sich die Datei auch nicht mehr in eineem Fehlerzustand.
|
So wie ich das sehe, wird die Datei vollkommen unabhängig von
eventuellen Fehlerzuständen schon von der C++-Laufzeitbibliothek
geschlossen, in dem Moment wie nämlich das std::fstream-Objekt ablebt.
| Quote: | Wenn man jetzt nochmals ein Programm startet, das diese Datei öffnet,
ist diese Datei nach dem Öffen nicht mehr in einem Fehlerzustand (im
Gegensatz zu 1)).
|
Ja.
Um Missverständnissen vorzubeugen: nicht "die Datei" befindet sich ggf.
in einem Fehlerzustand, sondern nur das std::fstream-Objekt. Auf vielen
Betriebssystemen kannst du eine Datei mit mehreren std::fstream öffnen.
Wenn einer davon im Fehlerzustand ist, funktionieren die anderen dennoch
weiter.
| Quote: | Wie soll man es sonst machen?
Zeilenweise lesen und parsen.
Das verstehe ich nicht:
Meinst du, dass man die ganze Datei Byte für Byte in ein Feld (array)
auslesen soll und danach dieses Feld analysieren und dann erst
gegebenenfalls auf die Datei zugreifen soll.
|
Nein. Du sollst die Datei zeilenweise lesen:
std::ifstream in("...");
std::string s;
while (getline(in, s)) {
// mach was mit s
}
Wenn du die Zeilen dann zu Fuß bzw. über std::stringstream
auseinandernimmst, zerlegt dir ein Syntax-Fehler nicht mehr den Status
des Eingabestreams.
Stefan
PS, der Code hat vermutlich auch das besprochene Problem, bei einem
Dateiende mitten auf einer Zeile in einen Fehlerzustand zu gehen und so
die Zeile nicht zu verarbeiten, Workaround wäre dann etwas wie
while (getline(in, s) || s.size()) {
// mach was mit s
s.clear();
} |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Mon Nov 13, 2006 10:04 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
| Quote: | Das verstehe ich nicht:
Meinst du, dass man die ganze Datei Byte für Byte in ein Feld (array)
auslesen soll und danach dieses Feld analysieren und dann erst
gegebenenfalls auf die Datei zugreifen soll.
Nein. Du sollst die Datei zeilenweise lesen:
std::ifstream in("...");
std::string s;
------->Variante1
while (getline(in, s)) {
// mach was mit s
}
Wenn du die Zeilen dann zu Fuß bzw. über std::stringstream
auseinandernimmst, zerlegt dir ein Syntax-Fehler nicht mehr den Status
des Eingabestreams.
PS, der Code hat vermutlich auch das besprochene Problem, bei einem
Dateiende mitten auf einer Zeile in einen Fehlerzustand zu gehen und so
die Zeile nicht zu verarbeiten, Workaround wäre dann etwas wie
------->Variante2
while (getline(in, s) || s.size()) {
// mach was mit s
s.clear();
Muss es nicht in.clear heisen, statt s.clear?
Variante 2 ist überflüssig (d.h.es reicht Variante1) oder hast du ein |
Gegenbeispiel?
D.h. es müsste folgendes von dir vorgeschlagenes funktionieren:
while (getline(in, s)) {
// mach was mit s
}
Bem:
Ich habe in eine Datei "-3.5e" bzw. "-3.5e2" bzw. "-3.5e "
reingeschrieben.
Die Fehlerbits werden in allen zwei deiner vorgeschlagenen Varianten
gleich gesetzt.
| Quote: |
Wenn man mit getline zeilenweise ausliest, kann man den ganzen |
Dateiinhalt auslesen (einschließlich dem letzten Byte in der Datei)
ohne einen Formatfehler zu erhalten, weil man ja keine Zeichen in eine
Zahl umwandelt.
Wenn dann das letzte Byte ausgelesen wurde und dann danach eofbit
gesetzt wird und man dann nochmals ausliest, wird dann zwar auch
failbit gleich 1 gesetzt, aber das kommt nicht davon her, dass in der
Datei Daten nicht richtig in eine Zahl umgewandelt werden.
Ergebnis:
Ein gesetztes failbit kommt also nicht davon her, dass ein
Formatfehler passierte, sondern dass beim Lesen festgestellt wurde,
dass eofbit oder badbit gleich 1 war (und dann failbit auf 1 gesetzt
wird).
Deswegen muss man also das failbit nicht mehr testen.
Ein Programm mit Fehlerreport sieht also so aus:
#include "stdafx.h"
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main(){
string mystring;
fstream quelle;
quelle.open("test.txt", ios::out|ios::app|ios::binary);
quelle.close();
quelle.open("test.txt", ios::in|ios::out|ios::trunc|ios::binary);
quelle << "-3.5e" << flush;
quelle.seekg(0, ios::beg);
cout << "eofbit=" << quelle.eof() << endl;
cout << "badbit=" << quelle.bad() << endl;
cout << "fail=" << quelle.fail() << endl;
// Auslesen der Datei
while(getline(quelle, mystring)){
//while(getline(quelle, mystring)||mystring.size()){
cout << "_mystring=" << mystring << endl;
cout << "_eofbit=" << quelle.eof() << endl;
cout << "_badbit=" << quelle.bad() << endl;
cout << "_fail=" << quelle.fail() << endl;
//quelle.clear();
}
cout << "eofbit=" << quelle.eof() << endl;
cout << "badbit=" << quelle.bad() << endl;
cout << "fail=" << quelle.fail() << endl;
// Fehlerreport
if (quelle.bad()){
cout << "Es gab ein technisches Problem\n"; // (*)
}
// failbit braucht nicht mehr abgeprüft zu werden,
// sondern nur noch badbit
return 0 ;
}
Ist das richtig, oder wie heisst das dann Schema des Fehlerreports?
mfg
Ernst |
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Thu Nov 16, 2006 11:26 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
Hallo,
Ernst Baumann wrote:
| Quote: | std::ifstream in("...");
std::string s;
------->Variante1
while (getline(in, s)) {
// mach was mit s
}
------->Variante2
while (getline(in, s) || s.size()) {
// mach was mit s
s.clear();
Muss es nicht in.clear heisen, statt s.clear?
|
Hintergedanke war, hier korrekt rauszugehen falls getline einen Fehler
liefert, aber dennoch ein Schnipselchen Text in 's' abgelegt hat. Dazu
sollte man natürlich den String nachher leer machen, damit er, falls
getline irgendwann mal nichts mehr tut wegen Fehler, nicht in eine
Endlosschleife geht.
| Quote: | Variante 2 ist überflüssig (d.h.es reicht Variante1) oder hast du ein
Gegenbeispiel?
|
Vermutlich ist es überflüssig (ich bin bei Stream-Interna auch kein
Profi und lerne noch eine Menge dazu). Nach dem letzten getline ist zwar
das EOF-Bit gesetzt, nicht aber das Fail-Bit, daher wird auch in
Variante 1 die Datei korrekt verarbeitet, auch wenn das \n am Ende der
Datei fehlt. Solche Dateien habe ich aber nur selten, weil ich meine
Editoren darauf konfiguriert habe, das immer anzuhängen :)
| Quote: | D.h. es müsste folgendes von dir vorgeschlagenes funktionieren:
while (getline(in, s)) {
// mach was mit s
}
|
Ja.
Stefan |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Fri Nov 17, 2006 4:10 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
| Quote: |
Muss es nicht in.clear heisen, statt s.clear?
Hintergedanke war, hier korrekt rauszugehen falls getline einen Fehler
liefert, aber dennoch ein Schnipselchen Text in 's' abgelegt hat. Dazu
sollte man natürlich den String nachher leer machen, damit er, falls
getline irgendwann mal nichts mehr tut wegen Fehler, nicht in eine
Endlosschleife geht.
Das Problem ist: |
clear()
gibt eine Fehlermeldung.
Im folgenden Programm meldet mir der Compiler VC++ Vers. 6.0:
"error C2039: 'clear' : Ist kein Element von 'basic_string<char,struct
std::char_traits<char>,class std::allocator<char> >'"
---------- Programm-Beginn -----------
#include "stdafx.h"
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main(){
string mystring;
fstream quelle;
quelle.open("test.txt", ios::out|ios::app|ios::binary);
quelle.close();
quelle.open("test.txt", ios::in|ios::out|ios::trunc|ios::binary);
quelle << "-3.5e" << flush;
quelle.seekg(0, ios::beg);
mystring.clear(); // <-- Hier meldet der Compiler einen Fehler.
return 0 ;
}
---------- Programm-Ende -----------
Was muss ich tun, damit der Compiler bei
mystring.clear();
keinen Fehler meldet?
| Quote: |
Variante 2 ist überflüssig (d.h.es reicht Variante1) oder hast du ein
Gegenbeispiel?
Vermutlich ist es überflüssig (ich bin bei Stream-Interna auch kein
Profi und lerne noch eine Menge dazu). Nach dem letzten getline ist zwar
das EOF-Bit gesetzt, nicht aber das Fail-Bit,
Wenn er das letzte Mal in die Schleife kommt (also wenn die Bedinung |
das letzte Mal wahr wird) ist das EOF-Bit gesetzt, nicht aber das
Fail-Bit. Dann wird aber nochmals die Bedingung abgeprüft (die dann
falsch wird) und dann ist aber das EOF-Bit und das Fail-Bit gesetzt.
| Quote: |
daher wird auch in
Variante 1 die Datei korrekt verarbeitet, auch wenn das \n am Ende der
Datei fehlt. Solche Dateien habe ich aber nur selten, weil ich meine
Editoren darauf konfiguriert habe, das immer anzuhängen :)
D.h. es müsste folgendes von dir vorgeschlagenes funktionieren:
while (getline(in, s)) {
// mach was mit s
}
Meine Frage ist dann nochmals folgende: |
Wie müsste dann der Fehlerreport aussehen?
So wie ich es in meinem letzten Posting (am Ende) geschrieben habe?
mfg
Ernst |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Tue Nov 21, 2006 12:15 am Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
| Quote: | Meine Frage ist dann nochmals folgende:
Wie müsste dann der Fehlerreport aussehen?
So wie ich es in meinem letzten Posting (am Ende) geschrieben habe?
Würde ich annehmen. Ich würde, wenn ich an so einer Stelle
Fehlerbehandlung haben wollen wöllte, vermutlich etwas wie
if (!in.eof()) {
perror("...");
}
Ich will nicht spitzfindig werden, aber nur nochmals um mein |
Verständnis zu überprüfen:
....
// Auslesen der Datei
while(<Datei quelle auslesen>){
//Fehlerreport
if (quelle.bad()){
cout << "Es gab ein technisches Problem\n"; // (*)
}
....
Ich glaube nicht, dass es sinnvoll ist statt meinem Fehlerreprt:
if (quelle.bad()){
...
}
den Fehlerreport von dir
if (!in.eof()) {
...
}
in der Schleife zu bringen, weil nach dem Auslesen des letzten Bytes
der Datei das eofbit = 1 gesetzt wird und wenn dabei auch gleichzeitig
ein "schlimmer (bad) Fehler" auftritt (wie z.B. dass gerade in dem
Moment die Festplatte kaputtgeht oder die Diskette aus dem Laufwerk
gezogen wird), wird auch badbit = 1 gesetzt.
Bei deiner Version würde dieser Fall nicht abgefangen werden, bei
meiner Version würde dieser Fall abgefangen werden.
PS:
failbit=1 braucht nicht mehr abgeprüft zu werden, da es keinen
Formatierungsfehler (z.B. Zeichenkette kann nicht in eine Zahl
konvertiert werden) geben kann, weil nur die Daten als Zeichenketten
gelesen werden.
Was meinst du dazu ?
mfg
Ernst |
|
| Back to top |
|
 |
Stefan Reuther Guest
|
Posted: Wed Nov 22, 2006 8:07 pm Post subject: Re: Wie Fehlerreport machen bei Dateizugriff? |
|
|
Ernst Baumann wrote:
| Quote: | Ich glaube nicht, dass es sinnvoll ist statt meinem Fehlerreprt:
if (quelle.bad()){
...
}
den Fehlerreport von dir
if (!in.eof()) {
...
}
in der Schleife zu bringen, weil nach dem Auslesen des letzten Bytes
der Datei das eofbit = 1 gesetzt wird und wenn dabei auch gleichzeitig
ein "schlimmer (bad) Fehler" auftritt (wie z.B. dass gerade in dem
Moment die Festplatte kaputtgeht oder die Diskette aus dem Laufwerk
gezogen wird), wird auch badbit = 1 gesetzt.
|
Das würde ich dir ohne weiteres glauben. Wie gesagt, in Stream-Interna
bin ich nicht so firm, da ich eh meist Binär-I/O mit eigenen
Abstraktionen mache.
Stefan |
|
| Back to top |
|
 |
Powered by phpBB © 2001, 2006 phpBB Group
|