 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ernst Baumann Guest
|
Posted: Tue Jan 31, 2006 1:28 am Post subject: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
Hallo allerseits,
wenn man z.B. in einer Datei eine Zahl, dann eine Zeichenkette, dann
wieder eine Zahl abgespeichert hat, kann man z.B. in C mit:
fscanf(fp, %d %s%d", &a, str, &b)
diese Werte (_verschiedene_ Datentypen) wieder auslesen.
Ich habe versucht, dies in C++ wie folgt zu realisieren:
1)
zuerst beschreiben der Datei mit Testdaten:
z1= 134;
z2= 125;
quelle.write((char*)&z1, sizeof(z1));
quelle << "01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
2) dann lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
Leider wird nur die 1. Zahl richtig gelesen und die Zeichenkette.
Die 2. Zahl wird falsch gelesen.
Frage:
Warum ist mein Programm falsch ?
Wie kann man es (möglichst einfach) reparieren ?
PS:
Hier ist das vollständige Programm:
-------------Programm-Anfang-----------------
#include "stdafx.h"
#include <string>
#include <iostream> // i/o
#include <fstream> // datei i/o
#include <string.h>
using namespace std;
int main(){
int beenden=0;
fstream quelle, ziel;
int z1, z2;
string str;
// Quelldatei zum lesen und schreiben öffnen. Sie muß existieren
// Wenn sie nicht existiert, gibt es einen Fehler.
// Öffnen als Textdatei
quelle.open("testdat.txt", ios::in|ios::out);
if(quelle.fail()){
cout << "Quelldatei existiert nicht" << endl;
cout << "Programm wird beendet" << flush;
beenden=1;
}
else { // Quelldatei existiert
// Schreibe Testdaten
z1= 134;
z2= 125;
quelle.write((char*)&z1, sizeof(z1));
quelle << "01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
z1 = 280;
z2 = 223;
quelle.write((char*)&z1, sizeof(z1));
quelle << "05.03.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
z1 = 134;
z2 = 10;
quelle.write((char*)&z1, sizeof(z1));
quelle << "06.04.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
z1 = 111;
z2 = 410;
quelle.write((char*)&z1, sizeof(z1));
quelle << "07.05.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
}
// Dateizeiger auf Dateianfang setzen
quelle.seekg(0, ios::beg);
while(beenden == 0){
// Lies Card-ID aus
quelle.read((char*)&z1, sizeof(z1));
// Jetzt Fehlerreport
if (quelle.fail()){
if (!quelle.eof()){
if (quelle.bad())
cout << "Es gab ein technisches Problem\n"; // (*)
else
cout << "Format-Fehler!\n";
}
else{ // Dateiende erreicht
beenden = 1;
cout << z1;
}
}
if(beenden == 0){
// Lies Zeit aus (Zeichenkette) aus
quelle >> str;
// Jetzt Fehlerreport
if (quelle.fail()){
if (!quelle.eof()){
if (quelle.bad())
cout << "Es gab ein technisches Problem\n"; // (*)
else
cout << "Format-Fehler!\n";
}
else{ // Dateiende erreicht
beenden = 1;
cout << str;
}
}
}
if(beenden == 0){
// Lies z2 aus
quelle.read((char*)&z2, sizeof(z2));
// Jetzt Fehlerreport
if (quelle.fail()){
if (!quelle.eof()){
if (quelle.bad())
cout << "Es gab ein technisches Problem\n"; // (*)
else
cout << "Format-Fehler!\n";
}
else{ // Dateiende erreicht
beenden = 1;
cout << z2;
}
}
}
}
return(0);
}
-------------Programm-Anfang-----------------
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Heinz Ozwirk Guest
|
Posted: Tue Jan 31, 2006 3:02 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
"Ernst Baumann" <carlox (AT) web (DOT) de> schrieb im Newsbeitrag
news:djtst1ht0tqjjvmq4kdpm8vtkcpe4u2bgn (AT) 4ax (DOT) com...
| Quote: | Hallo allerseits,
wenn man z.B. in einer Datei eine Zahl, dann eine Zeichenkette, dann
wieder eine Zahl abgespeichert hat, kann man z.B. in C mit:
fscanf(fp, %d %s%d", &a, str, &b)
diese Werte (_verschiedene_ Datentypen) wieder auslesen.
Ich habe versucht, dies in C++ wie folgt zu realisieren:
1)
zuerst beschreiben der Datei mit Testdaten:
z1= 134;
z2= 125;
quelle.write((char*)&z1, sizeof(z1));
quelle << "01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
2) dann lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
|
Das ist etwas ganz anderes als das, was in C angeblich funktioniert. Der
fscanf Aufruf erwartet, dass die beiden Zahlen als Strings mit dem Wert der
Zahl in Dezimaldarstellung in die Datei geschrieben wurden (was mit fprintf
vermutlich aus so passiert ist). Die C++ Version erwartet dagegen, dass die
beiden Zahlen in ihrer internen Darstellung im Speicher (also binär) in die
Datei geschrieben und so auch wieder gelesen werden. Ob das funktionierten
kann, hängt davon ab wie die Datei geöffnet wurde.
Man sollte eigentlich immer misstrauisch werden, wenn read oder write und >>
oder << mit dem gleichen Stream verwendet werden. In solchen Fällen ist
häufig etwas faul.
Eins deiner Probleme ist, dass "quelle >> str" nicht wirklich weiß, wo der
String aufhört und die nächste Zahl anfängt. Daher liest diese Anweisung
alles bis zum nächsten Leerzeichen oder bis zum Zeilenende aus der Datei und
speichert es in der String-Variablen. Das heißt, dass in mehr als 99% aller
Fälle mindestens ein Byte, das eigentlich schon Teil der zweiten Zahl ist,
noch als Teil des Strings gelesen wird. Dadurch liefert natürlich auch das
read für die zweite Zahl falsche Ergebnisse und im Laufe der Zeit wird das
immer schlimmer.
Es wäre wohl besser die Daten so zu schreiben:
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << std::endl;
und entsprechend auch wieder zu lesen:
quelle >> z1 >> str >> z2;
[...]
| Quote: | // Quelldatei zum lesen und schreiben öffnen. Sie muß existieren
// Wenn sie nicht existiert, gibt es einen Fehler.
// Öffnen als Textdatei
quelle.open("testdat.txt", ios::in|ios::out);
|
Hier liegt ein weiteres Problem. Du öffnest die Datei, wie der Kommentar
richtig beschreibt, als Textdatei, schreibst dann aber binäre Daten hinein.
Beim Schreiben in eine Textdatei können aber Zeichen geändert werden. So ist
es bei einigen Systemen üblich, dass '\n' durch die zwei Zeichen '\x0D'
'\x0A' ersetzt wird. Und das passiert auch, wenn man Binärdaten in eine
Textdatei schreibt. Das bringt natürlich die Daten in der Datei ein bisschen
durcheinander.
HTH
Heinz
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Tue Jan 31, 2006 4:01 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
Ernst Baumann wrote:
| Quote: | wenn man z.B. in einer Datei eine Zahl, dann eine
Zeichenkette, dann wieder eine Zahl abgespeichert hat, kann
man z.B. in C mit:
fscanf(fp, %d %s%d", &a, str, &b)
|
Für gewisse Zeichenketten, ebenfalls. In jedem Fall kommt es
daran, wie die Datei geschrieben wurde. Wenn sie mit
fprintf( fp, "%d %s %d\n", a, str, b ) ;
geschrieben wurde, und str nie ein Leerzeichen enthält, dann
klappt's. Am sonsten nicht.
| Quote: | diese Werte (_verschiedene_ Datentypen) wieder auslesen.
Ich habe versucht, dies in C++ wie folgt zu realisieren:
1)
zuerst beschreiben der Datei mit Testdaten:
z1= 134;
z2= 125;
quelle.write((char*)&z1, sizeof(z1));
quelle << "01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
|
Da aber schreibst du was ganz anders. Diese Datei kannst du
nicht mit deinem fscanf lesen.
Warum nicht einfach:
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << std::endl ;
Das entspricht den fprintf oben.
| Quote: | 2) dann lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
Leider wird nur die 1. Zahl richtig gelesen und die
Zeichenkette. Die 2. Zahl wird falsch gelesen.
|
Schon bei der Zeichenkette vermute ich, dass es Probleme gibt.
Dagegen, wenn du wie oben die Daten geschrieben hast, dann geht
auch:
quelle >> z1 >> str >> z2 ;
schon richtig.
In der Praxis benutzt man istream::read und ostream::write kaum.
| Quote: | Frage:
Warum ist mein Programm falsch ?
|
Einfache Antwort: weil du binär geschrieben und gelesen hast, da
wo es nicht zupasst. Du musst wohl von Haus aus entscheiden, was
du als Format benutzen will. Danach musst du darum kummern, dass
was geschrieben wird, dieses Format zupasst. Dann beim Lesen
musst du es respektieren.
Merk wohl, dass die erste Schritt immer ist, das Format
festzulegen. Bevor du das gemacht hat, hat alle andere kein
Sinn.
--
James Kanze GABI Software
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
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Tue Jan 31, 2006 9:01 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
| Quote: | Das ist etwas ganz anderes als das, was in C angeblich funktioniert. Der
fscanf Aufruf erwartet, dass die beiden Zahlen als Strings mit dem Wert der
Zahl in Dezimaldarstellung in die Datei geschrieben wurden (was mit fprintf
vermutlich aus so passiert ist). Die C++ Version erwartet dagegen, dass die
beiden Zahlen in ihrer internen Darstellung im Speicher (also binär) in die
Datei geschrieben und so auch wieder gelesen werden.
Ob das funktionierten kann, hängt davon ab wie die Datei geöffnet wurde.
1) Meinst du damit, ich soll die Datei im Binärmodus öffnen ? |
Oder was willst du damit genau sagen ?
| Quote: |
Man sollte eigentlich immer misstrauisch werden, wenn read oder write und
oder << mit dem gleichen Stream verwendet werden. In solchen Fällen ist
häufig etwas faul.
2) Warum ? Wenn man Zahlen von Zeichenketten durch white spaces |
trennt, dürfte es keine Probleme geben, oder ?
| Quote: |
Eins deiner Probleme ist, dass "quelle >> str" nicht wirklich weiß, wo der
String aufhört und die nächste Zahl anfängt. Daher liest diese Anweisung
alles bis zum nächsten Leerzeichen oder bis zum Zeilenende aus der Datei und
speichert es in der String-Variablen. Das heißt, dass in mehr als 99% aller
Fälle mindestens ein Byte, das eigentlich schon Teil der zweiten Zahl ist,
noch als Teil des Strings gelesen wird. Dadurch liefert natürlich auch das
read für die zweite Zahl falsche Ergebnisse und im Laufe der Zeit wird das
immer schlimmer.
3) Gut, das sehe ich ein. Eine Zahl muss von einer Zeichenkette |
getrennt werden, z.B. durch ein white space wie z.B. dem Leerzeichen
(space, ich glaube ASCII-Code 32 dezimal)
Dann müsste aber das Schreiben mit:
quelle.write((char*)&z1, sizeof(z1));
// BEACHTE: ein Leerzeichen _vor_ und nach der _Zeichenkette_
quelle << " 01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
und das lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
aber auch funktionieren.
Das tut es aber nicht.
Warum ??
| Quote: |
Es wäre wohl besser die Daten so zu schreiben:
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << std::endl;
4) |
a) das könnte man doch auch wie folgt machen:
quelle << z1 << " 01.02.2004 " << z2 << std::endl;
hier habe ich auch ein Leerzeichen _vor_ und nach der _Zeichenkette_
b) Mit der Methode von dir schreibst du Zahlen als Zeichenkette in die
Datei und liest die Zahlen als Zeichenkette aus der Datei, wo sie dann
als Zahlen in der entsprechenden Variablen abgespeichert werden (in
dem Datentyp, den die Variable hat). Ist das richtig ?
c) Die Methode von dir (und James) kann ich also benutzen (Danke für
den Tipp). Trotzdem ist mir immer noch unklar, warum meine Version
nicht funktioniert (siehe 3)), wenn ich die Zeichenkette (vorne und
hinten) durch ein white space von den Zahlen trenne.
| Quote: |
und entsprechend auch wieder zu lesen:
quelle >> z1 >> str >> z2;
ok.
// Öffnen als Textdatei
quelle.open("testdat.txt", ios::in|ios::out);
Hier liegt ein weiteres Problem. Du öffnest die Datei, wie der Kommentar
richtig beschreibt, als Textdatei, schreibst dann aber binäre Daten hinein.
Beim Schreiben in eine Textdatei können aber Zeichen geändert werden. So ist
es bei einigen Systemen üblich, dass '\n' durch die zwei Zeichen '\x0D'
'\x0A' ersetzt wird. Und das passiert auch, wenn man Binärdaten in eine
Textdatei schreibt. Das bringt natürlich die Daten in der Datei ein bisschen
durcheinander.
Du hast recht. Ich muss dafür (also bei meiner Version, die nicht |
funktioniert), den Binärmodus benutzen.
mfg
Ernst
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Heinz Ozwirk Guest
|
Posted: Wed Feb 01, 2006 4:31 am Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
"Ernst Baumann" <carlox (AT) web (DOT) de> schrieb im Newsbeitrag
news:5fhvt1lnk8a4gdupl1r1p6vosf7v6bhutn (AT) 4ax (DOT) com...
| Quote: | Das ist etwas ganz anderes als das, was in C angeblich funktioniert. Der
fscanf Aufruf erwartet, dass die beiden Zahlen als Strings mit dem Wert
der
Zahl in Dezimaldarstellung in die Datei geschrieben wurden (was mit
fprintf
vermutlich aus so passiert ist). Die C++ Version erwartet dagegen, dass
die
beiden Zahlen in ihrer internen Darstellung im Speicher (also binär) in
die
Datei geschrieben und so auch wieder gelesen werden.
Ob das funktionierten kann, hängt davon ab wie die Datei geöffnet wurde.
1) Meinst du damit, ich soll die Datei im Binärmodus öffnen ?
Oder was willst du damit genau sagen ?
|
Ob du die Datei im Binärmodus öffnen musst oder nicht, hängt nicht davon ab,
was ich meine, sondern was dein Datenformat vorschreibt. Wenn dein
Datenformat von dir verlangt, dass die Zahlen binär (häufig also als Folge
von 4 Bytes) in der Datei stehen sollen, dann musst du die Datei auch binär
öffnen, sonst gibt es ein Durcheinander und das liegt nicht nur an fehlenden
Leerzeichen. Wenn dein Datenformat aber vorschreibt, dass Zahlen erst in
Zeichenketten konvertiert werden sollen, dann musst du den Textmodus (also
default) nehmen. Da du als Beispiel für die C Version fscanf genommen hast
und fscanf erwartet, dass Zahlen als Zeichenketten vorliegen, gehe ich
natürlicherst einmal davon aus, dass ein entsprechendes C++ Programm sich
auch so verhalten soll.
| Quote: | Man sollte eigentlich immer misstrauisch werden, wenn read oder write und
oder << mit dem gleichen Stream verwendet werden. In solchen Fällen ist
häufig etwas faul.
2) Warum ? Wenn man Zahlen von Zeichenketten durch white spaces
trennt, dürfte es keine Probleme geben, oder ?
|
read und write sind hauptsächlich für Binärdateien vorgesehen, operator<<
und operator>> sowie endl für Textdateien. Wenn man beides gleichzeitig in
einem Stream verwendet, heißt das, dass man in eine Datei gleichzeitig
Binärdaten und Textdaten schreibt. Die Datei ist dann also weder eine Text-
noch eine Binärdatei. Und Leerzeichen als Begrenzer sind auch nicht wirklich
eine Lösung, jedenfalls dann nicht, wenn der String selbst auch Leerzeichen
enthalten kann. Natürlich kann es manchmal funktionieren, aber gewöhnlich
treten die Probleme dann gerade dann auf, wenn man sie am wenigsten
gebrauchen kann.
| Quote: |
Eins deiner Probleme ist, dass "quelle >> str" nicht wirklich weiß, wo der
String aufhört und die nächste Zahl anfängt. Daher liest diese Anweisung
alles bis zum nächsten Leerzeichen oder bis zum Zeilenende aus der Datei
und
speichert es in der String-Variablen. Das heißt, dass in mehr als 99%
aller
Fälle mindestens ein Byte, das eigentlich schon Teil der zweiten Zahl ist,
noch als Teil des Strings gelesen wird. Dadurch liefert natürlich auch das
read für die zweite Zahl falsche Ergebnisse und im Laufe der Zeit wird das
immer schlimmer.
3) Gut, das sehe ich ein. Eine Zahl muss von einer Zeichenkette
getrennt werden, z.B. durch ein white space wie z.B. dem Leerzeichen
(space, ich glaube ASCII-Code 32 dezimal)
Dann müsste aber das Schreiben mit:
quelle.write((char*)&z1, sizeof(z1));
// BEACHTE: ein Leerzeichen _vor_ und nach der _Zeichenkette_
quelle << " 01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
und das lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
aber auch funktionieren.
Das tut es aber nicht.
Warum ??
|
Das Leerzeichen vor dem String brauchst du nicht, denn read weiß ja, wie
viele Zeichen es lesen soll. Aber das Leerzeichen nach dem String ist ein
Problem. Das Ende des Strings muss so gekennzeichnet werden, dass operator>>
das Ende auch erkennt. Mit einem Leerzeichen liest operator>> dann richtig
bis zum Leerzeichen und der String sollte richtig sein. Aber das Leerzeichen
bleibt im Stream stehen und wird beim nächsten read gelesen. Du bekommst
dann nicht die sizeof(z2) Bytes, die ursprünglich geschrieben wurden,
sondern eine Zahl, deren sizeof(z2) Bytes aus dem Leerzeichen und den ersten
sizeof(z2)-1 Bytes der ursprünglich geschriebenen Zahl bestehen. Das letzte
geschrieben Byte der Zahl bleibt dann für die nächste Eingabe in Stream
zurück. Ähnliche Probleme bereiten vermutlich auch die Zeilenenden.
Ein fiktives Beispiel:
short x = 45054; // angenommen sizeof(short) == 2
fstream s;
s.open(...);
s.write((char*) &x, 2); s << endl;
s.write((char*) &x, 2); s << endl;
s.write((char*) &x, 2); s << endl;
Unter Windows steht in der Datei dann
FE AF 0D 0A FE AF 0D 0A FE AF 0D 0A
Wenn man das wie in deinem ursprünglichen Code (ohne Tests) so liest
short a, b, c;
s.read((char*) &a, 2);
s.read((char*) &b, 2);
s.read((char*) &c, 2);
cout << a << ' ' b << ' ' << c << endl;
bekommt man
45054 2573 45054
Der erste Wert ist das, was man als erstes geschrieben hat. Und die 2573?
Das sind die Bytes 0D 0A. Und die zweite 45054 ist das, was als zweiter Wert
geschrieben wurde. Und jetzt viel Spaß beim Beheben dieses Problems.
| Quote: | Es wäre wohl besser die Daten so zu schreiben:
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << std::endl;
4)
a) das könnte man doch auch wie folgt machen:
quelle << z1 << " 01.02.2004 " << z2 << std::endl;
hier habe ich auch ein Leerzeichen _vor_ und nach der _Zeichenkette_
|
Wenn der String wirklich als Zeichenkette vorliegt, ja, aber solche
magischen Literale kommen in meinen Programmen in der Regel nicht vor.
Normlerweise stammen solche Daten aus Variablen. Und selbst wenn nicht - die
Schreibweise mit den separaten Separatoren macht deutlicher, dass die
Leerzeichen nicht zum String gehören sondern nur Trennzeichen sind. Das
Ergebnis ist natürlich in beiden Fällen das gleiche.
| Quote: | b) Mit der Methode von dir schreibst du Zahlen als Zeichenkette in die
Datei und liest die Zahlen als Zeichenkette aus der Datei, wo sie dann
als Zahlen in der entsprechenden Variablen abgespeichert werden (in
dem Datentyp, den die Variable hat). Ist das richtig ?
|
Ja.
| Quote: | c) Die Methode von dir (und James) kann ich also benutzen (Danke für
den Tipp). Trotzdem ist mir immer noch unklar, warum meine Version
nicht funktioniert (siehe 3)), wenn ich die Zeichenkette (vorne und
hinten) durch ein white space von den Zahlen trenne.
|
Ich hoffe, das Beispiel hat einen kleinen Einblick in ein paar der Probleme
gegeben.
| Quote: | und entsprechend auch wieder zu lesen:
quelle >> z1 >> str >> z2;
ok.
// Öffnen als Textdatei
quelle.open("testdat.txt", ios::in|ios::out);
Hier liegt ein weiteres Problem. Du öffnest die Datei, wie der Kommentar
richtig beschreibt, als Textdatei, schreibst dann aber binäre Daten
hinein.
Beim Schreiben in eine Textdatei können aber Zeichen geändert werden. So
ist
es bei einigen Systemen üblich, dass '\n' durch die zwei Zeichen '\x0D'
'\x0A' ersetzt wird. Und das passiert auch, wenn man Binärdaten in eine
Textdatei schreibt. Das bringt natürlich die Daten in der Datei ein
bisschen
durcheinander.
Du hast recht. Ich muss dafür (also bei meiner Version, die nicht
funktioniert), den Binärmodus benutzen.
|
In einer Version, die nicht funktioniert, ist es eigentlich egal, wie man es
falsch macht ;-)
Selbst wenn du die Datei im Binärmodus öffnest und das Problem mit dem
Leerzeichen löst, hast du noch ein Problem mit dem Zeilenende. In der Datei
steht dann zwar nicht 0D 0A sondern nur 0A, aber immer noch ein Zeichen zu
viel. Natürlich lässt sich auch dieses Problem lösen, aber was ist einfacher
zu schreiben und zu verstehen?
quelle.read((char*) &z1, sizeof(z1));
quelle >> str;
quelle.ignore(1);
quelle.read((char*) &z2, sizeof(z2));
quelle.ignore(1);
oder
quelle >> z1 >> str >> z2;
HTH
Heinz
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Wed Feb 01, 2006 11:02 am Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
Ernst Baumann wrote:
| Quote: | Das ist etwas ganz anderes als das, was in C angeblich
funktioniert. Der fscanf Aufruf erwartet, dass die beiden
Zahlen als Strings mit dem Wert der Zahl in
Dezimaldarstellung in die Datei geschrieben wurden (was mit
fprintf vermutlich aus so passiert ist). Die C++ Version
erwartet dagegen, dass die beiden Zahlen in ihrer internen
Darstellung im Speicher (also binär) in die Datei geschrieben
und so auch wieder gelesen werden.
Ob das funktionierten kann, hängt davon ab wie die Datei
geöffnet wurde.
1) Meinst du damit, ich soll die Datei im Binärmodus öffnen ?
Oder was willst du damit genau sagen ?
|
Wahrscheinlich, dass die Datei im Binärmodus geöffnet werden
muss. Obwohl dass nicht immer ausreicht.
| Quote: | Man sollte eigentlich immer misstrauisch werden, wenn read
oder write und >> oder << mit dem gleichen Stream verwendet
werden. In solchen Fällen ist häufig etwas faul.
2) Warum ?
Wenn man Zahlen von Zeichenketten durch white
spaces trennt, dürfte es keine Probleme geben, oder ?
|
Doch. Binäre und Text-Daten in der selben Datei zu mischen ist
wohl verdächtig. Man kann es tun, aber herauszufinden, wo die
einer aufhört und die andere anfäng is gar nicht trivial.
| Quote: | Eins deiner Probleme ist, dass "quelle >> str" nicht wirklich
weiß, wo der String aufhört und die nächste Zahl anfängt.
Daher liest diese Anweisung alles bis zum nächsten
Leerzeichen oder bis zum Zeilenende aus der Datei und
speichert es in der String-Variablen. Das heißt, dass in mehr
als 99% aller Fälle mindestens ein Byte, das eigentlich schon
Teil der zweiten Zahl ist, noch als Teil des Strings gelesen
wird. Dadurch liefert natürlich auch das read für die zweite
Zahl falsche Ergebnisse und im Laufe der Zeit wird das immer
schlimmer.
3) Gut, das sehe ich ein. Eine Zahl muss von einer
Zeichenkette getrennt werden, z.B. durch ein white space wie
z.B. dem Leerzeichen (space, ich glaube ASCII-Code 32 dezimal)
Dann müsste aber das Schreiben mit:
quelle.write((char*)&z1, sizeof(z1));
|
Warum willst du unbedingt binär schreiben. Das ist das Problem.
Wenn du ein binäres Format hast, kannst du auch eine
Formattierung für Zeichenketten vorsehen. Typischerweise wird es
aber nicht mit << kompatibel sein.
| Quote: | // BEACHTE: ein Leerzeichen _vor_ und nach der _Zeichenkette_
quelle << " 01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
und das lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
aber auch funktionieren.
Das tut es aber nicht. Warum ??
|
Wer weiß? Ich weiß noch nicht, was du in der Datei haben willst.
Beschreibe genau das Format, und dann können wir überlegen, was
du machen musst, es zu schreiben und zu lesen (oder merken wir,
dass es mehrdeutig ist).
| Quote: | Es wäre wohl besser die Daten so zu schreiben:
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << std::endl;
4)
a) das könnte man doch auch wie folgt machen:
quelle << z1 << " 01.02.2004 " << z2 << std::endl;
hier habe ich auch ein Leerzeichen _vor_ und nach der
_Zeichenkette_
|
In der Tat. Ich vermute, er wollte das Prinzip zeigen, das auch
gilt, wenn die Zeichenkette aus einer Variable kommt.
| Quote: | b) Mit der Methode von dir schreibst du Zahlen als
Zeichenkette in die Datei und liest die Zahlen als
Zeichenkette aus der Datei, wo sie dann als Zahlen in der
entsprechenden Variablen abgespeichert werden (in dem
Datentyp, den die Variable hat). Ist das richtig ?
|
Ja.
| Quote: | c) Die Methode von dir (und James) kann ich also benutzen
(Danke für den Tipp). Trotzdem ist mir immer noch unklar,
warum meine Version nicht funktioniert (siehe 3)), wenn ich
die Zeichenkette (vorne und hinten) durch ein white space von
den Zahlen trenne.
|
Vielleicht weil das trennende Leerzeichen nicht beim Lesen der
Kette entfernt wird, und danoch als Teil der Ganzzahl gelesen
wird? Ich bin aber nicht sicher. Wenn man aber binär und Text
mischt, muss man äußerst sorgfältig die Grenzen beachten.
Dazu: die interne Darstellung direkt in binär auszugeben
bereitet meistens nur Probleme in der Zukunft vor. Geht man auf
einen anderen Rechner, dann kann man die Daten nicht mehr lesen.
Es kann sogar vorkomment, dass man die lesen kann, wenn man eine
neue Version des Compilers benutzt, oder sogar andere Optionnen
bei der Compilierung.
--
James Kanze GABI Software
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
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Thu Feb 02, 2006 4:02 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
Heinz Ozwirk wrote:
| Quote: | "Ernst Baumann" <carlox (AT) web (DOT) de> schrieb im Newsbeitrag
news:5fhvt1lnk8a4gdupl1r1p6vosf7v6bhutn (AT) 4ax (DOT) com...
Wenn dein Datenformat aber vorschreibt, dass Zahlen erst in
Zeichenketten konvertiert werden sollen, dann musst du den
Textmodus (also default) nehmen.
|
Nicht unbedingt. Wenn du Textdaten nach einer Datei schreibst,
und die Datei nur lokal verwendet werden soll, dann sollst du
Textmodus nehmen. Wenn die Daten aber von mehreren
unterschiedenen Systemen gelesen werden sollen, Text oder nicht,
muss du binär verwenden, und selber um die Konventionene
kummern.
[...]
| Quote: | read und write sind hauptsächlich für Binärdateien vorgesehen,
|
Da bin ich nicht sicher. Endlich nehmen sie einen char*, und
nicht einen void*. Ich würde ehe denken, dass sie für
vorformattierten Textdaten gedacht sind.
Aber so klar ist es nicht. Weil innerhalb iostream wird auch
char benutzt, da wo es eindeutig um reine Bytes (und nicht
Textzeichen) geht, und wo man eher unsigned char erwartet hätte.
--
James Kanze GABI Software
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
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Georg Wall Guest
|
Posted: Thu Feb 02, 2006 5:52 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
Hallo,
dafür gibt es Klassen istringstream und ostringstream,beide in
<sstream>.
1. Du holst Dir mit getline(line,....) eine Zeile aus der Datei.
2. std::istringstream is(line); //
3 is>>intvara>>sonstvar>>uswvar; // Du könntest auch in ein Objekt
lesen, wenn Du diesem Objekt den >> operator spendierst.
Gruß
Georg
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Fri Feb 03, 2006 3:00 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
| Quote: |
Ob du die Datei im Binärmodus öffnen musst oder nicht, hängt nicht davon ab,
was ich meine, sondern was dein Datenformat vorschreibt.
Das Datenformat legt fest, wie man die Daten ablegt (also ob als |
Ganzzahl, Zeichenkette, double, ...). Ist das richtig ?
| Quote: |
Wenn dein
Datenformat von dir verlangt, dass die Zahlen binär (häufig also als Folge
von 4 Bytes) in der Datei stehen sollen, dann musst du die Datei auch binär
öffnen, sonst gibt es ein Durcheinander und das liegt nicht nur an fehlenden
Leerzeichen. Wenn dein Datenformat aber vorschreibt, dass Zahlen erst in
Zeichenketten konvertiert werden sollen, dann musst du den Textmodus (also
default) nehmen.
Ob ich die Datei im Textmodus oder Binärmodus öffne, ist völlg egal |
(habe ich ausprobiert, Textmodus bzw, Binärmodus betrifft nur, ob das
Newline-Zeichen als 2 Byte oder 1 Byte in die Datei abgelegt bzw. aus
der Datei gelesen wird)
| Quote: |
read und write sind hauptsächlich für Binärdateien vorgesehen, operator
und operator>> sowie endl für Textdateien.
Anhand deiner Tipps habe ich selbst experimentiert: |
Es ist egal: Man kann read, write, >>, << _sowohl_ im Binärmodus, als
auch im Textmodus verwenden.
Man kann read, write, >>, << auch gemeinsam in Binärdateien als auch
in Textdateien verwenden. Der Programmierer muss dann eben mehr
denken.
| Quote: |
Wenn man beides gleichzeitig in
einem Stream verwendet, heißt das, dass man in eine Datei gleichzeitig
Binärdaten und Textdaten schreibt. Die Datei ist dann also weder eine Text-
noch eine Binärdatei.
Das kann doch in der Praxis vorkommen, oder sollte man das vermeiden ? |
Übrigens:
Wann ist es sinnvoll Daten, z.B. Zahlen als Binärzahl abzulegen?
Man kann sie doch (wie du mir vorgeschlagen hast) als Zeichenkette
ablegen und dann wieder durch quelle << z1 als Zahl einlesen.
Wenn ich aber Zahlen als Zeichenketten ablegen kann, brauch ich auch
die Befehle read und write nicht mehr. (Ich will den von dir gemachten
Vorschalg "verallgemeiner", d.h. immer benutzen, oder wo hat er seine
Grenzen)
Ich habe etwas mit gemischten Daten (Binär- und Text) experimentiert:
1) Daten nur als Zeichenketen schreiben:
// Schreibe Testdaten
z1= 110;
z2= 111;
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << endl;
z1 = 112;
z2 = 113;
quelle << z1 << ' ' << "05.03.2004" << ' ' << z2 << endl;
}
// Dateizeiger auf Dateianfang setzen
quelle.seekg(0, ios::beg);
while(quelle >> z1 >> str >> z2){
cout << z1 << ' ' << str << ' ' << z2 << endl;
}
// Jetzt Fehlerreport
if (!quelle.eof()){
if (quelle.bad())
cout << "Es gab ein technisches Problem\n"; // (*)
else
cout << "Format-Fehler!\n";
}
a) Ist es sinnvoll, den Auslesevorgang so zu implementieren ?
b) Ist meine Fehlerverwaltung (Fehlerreport) so sinnvoll oder fehlt
noch etwas ?
2) Daten gemischt schreiben und lesen:
// Schreibe Testdaten
z1= 110;
z2= 111;
quelle.write((char*)&z1, sizeof(z1));
quelle << ' ';
quelle << "11.11.2011";
quelle << ' ';
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
z1= 112;
z2= 113;
quelle.write((char*)&z1, sizeof(z1));
quelle << ' ';
quelle << "22.22.2022";
quelle << ' ';
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
// Daten lesen:
// Dateizeiger auf Dateianfang setzen
quelle.seekg(0, ios::beg);
while(beenden == 0){
// Liest 1. zahl aus
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
// Zuerst Leerzeichen auslesen
quelle.read(&tempchar, 1);
// Lies z2 aus
quelle.read((char*)&z2, sizeof(z2));
// endl auslesen:
// Im Binärmodus sind das 1 Byte
// Im Textmodus sind das 2 Byte, doch nur das letzte wird ausgelesen
quelle.read(&tempchar, 1);
// Jetzt Fehlerreport
if (quelle.fail()){
if (!quelle.eof()){
if (quelle.bad())
cout << "Es gab ein technisches Problem\n"; // (*)
else
cout << "Format-Fehler!\n";
}
else{ // Dateiende erreicht
beenden = 1;
}
}
if (beenden==0)
cout << z1 << ' ' << str << ' ' << z2 << endl;
}
Frage:
a) Ist es sinnvoll den Auslesevorgang (mit der Variablen beenden) so
zu implementieren, oder ist es besser diese wie oben bei 1) zu machen,
wo in der while-Bedingung gelesen wird (wie kann man das
realisieren ?, dann brauch ich nicht mehr die Variable beenden)
b) Ist meine Fehlerverwaltung (Fehlerreport) so sinnvoll oder fehlt
noch etwas ?
c) Was mir nicht ganz klar ist:
Im Textmodus wird beim Befehl
quelle.read(&tempchar, 1);
das Newline-Zeichen eingelesen. Dies sind im Textmodus aber 2 Byte.
In tempchar wird aber nur 1 Byte einegelsen. Das ist mir auch noch
klar. Aber es wird nicht das 1. Byte eingelesen, sondern das 2. Byte.
Das ist mir nicht klar.
Ich hätte erwartet, dass man im Textmodus zweimal den Befehl schreiben
muss, also:
quelle.read(&tempchar, 1);
quelle.read(&tempchar, 1);
um alle 2 Bytes des Newline-Zeichens aufzunehmen.
Warum ist das so ?
mfg
Ernst
| Quote: | Und Leerzeichen als Begrenzer sind auch nicht wirklich
eine Lösung, jedenfalls dann nicht, wenn der String selbst auch Leerzeichen
enthalten kann. Natürlich kann es manchmal funktionieren, aber gewöhnlich
treten die Probleme dann gerade dann auf, wenn man sie am wenigsten
gebrauchen kann.
Eins deiner Probleme ist, dass "quelle >> str" nicht wirklich weiß, wo der
String aufhört und die nächste Zahl anfängt. Daher liest diese Anweisung
alles bis zum nächsten Leerzeichen oder bis zum Zeilenende aus der Datei
und
speichert es in der String-Variablen. Das heißt, dass in mehr als 99%
aller
Fälle mindestens ein Byte, das eigentlich schon Teil der zweiten Zahl ist,
noch als Teil des Strings gelesen wird. Dadurch liefert natürlich auch das
read für die zweite Zahl falsche Ergebnisse und im Laufe der Zeit wird das
immer schlimmer.
3) Gut, das sehe ich ein. Eine Zahl muss von einer Zeichenkette
getrennt werden, z.B. durch ein white space wie z.B. dem Leerzeichen
(space, ich glaube ASCII-Code 32 dezimal)
Dann müsste aber das Schreiben mit:
quelle.write((char*)&z1, sizeof(z1));
// BEACHTE: ein Leerzeichen _vor_ und nach der _Zeichenkette_
quelle << " 01.02.2004 ";
quelle.write((char*)&z2, sizeof(z2));
quelle << endl;
und das lesen mit:
quelle.read((char*)&z1, sizeof(z1));
quelle >> str;
quelle.read((char*)&z2, sizeof(z2));
aber auch funktionieren.
Das tut es aber nicht.
Warum ??
Das Leerzeichen vor dem String brauchst du nicht, denn read weiß ja, wie
viele Zeichen es lesen soll. Aber das Leerzeichen nach dem String ist ein
Problem. Das Ende des Strings muss so gekennzeichnet werden, dass operator
das Ende auch erkennt. Mit einem Leerzeichen liest operator>> dann richtig
bis zum Leerzeichen und der String sollte richtig sein. Aber das Leerzeichen
bleibt im Stream stehen und wird beim nächsten read gelesen. Du bekommst
dann nicht die sizeof(z2) Bytes, die ursprünglich geschrieben wurden,
sondern eine Zahl, deren sizeof(z2) Bytes aus dem Leerzeichen und den ersten
sizeof(z2)-1 Bytes der ursprünglich geschriebenen Zahl bestehen. Das letzte
geschrieben Byte der Zahl bleibt dann für die nächste Eingabe in Stream
zurück. Ähnliche Probleme bereiten vermutlich auch die Zeilenenden.
Ein fiktives Beispiel:
short x = 45054; // angenommen sizeof(short) == 2
fstream s;
s.open(...);
s.write((char*) &x, 2); s << endl;
s.write((char*) &x, 2); s << endl;
s.write((char*) &x, 2); s << endl;
Unter Windows steht in der Datei dann
FE AF 0D 0A FE AF 0D 0A FE AF 0D 0A
Wenn man das wie in deinem ursprünglichen Code (ohne Tests) so liest
short a, b, c;
s.read((char*) &a, 2);
s.read((char*) &b, 2);
s.read((char*) &c, 2);
cout << a << ' ' b << ' ' << c << endl;
bekommt man
45054 2573 45054
Der erste Wert ist das, was man als erstes geschrieben hat. Und die 2573?
Das sind die Bytes 0D 0A. Und die zweite 45054 ist das, was als zweiter Wert
geschrieben wurde. Und jetzt viel Spaß beim Beheben dieses Problems.
Es wäre wohl besser die Daten so zu schreiben:
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << std::endl;
4)
a) das könnte man doch auch wie folgt machen:
quelle << z1 << " 01.02.2004 " << z2 << std::endl;
hier habe ich auch ein Leerzeichen _vor_ und nach der _Zeichenkette_
Wenn der String wirklich als Zeichenkette vorliegt, ja, aber solche
magischen Literale kommen in meinen Programmen in der Regel nicht vor.
Normlerweise stammen solche Daten aus Variablen. Und selbst wenn nicht - die
Schreibweise mit den separaten Separatoren macht deutlicher, dass die
Leerzeichen nicht zum String gehören sondern nur Trennzeichen sind. Das
Ergebnis ist natürlich in beiden Fällen das gleiche.
b) Mit der Methode von dir schreibst du Zahlen als Zeichenkette in die
Datei und liest die Zahlen als Zeichenkette aus der Datei, wo sie dann
als Zahlen in der entsprechenden Variablen abgespeichert werden (in
dem Datentyp, den die Variable hat). Ist das richtig ?
Ja.
c) Die Methode von dir (und James) kann ich also benutzen (Danke für
den Tipp). Trotzdem ist mir immer noch unklar, warum meine Version
nicht funktioniert (siehe 3)), wenn ich die Zeichenkette (vorne und
hinten) durch ein white space von den Zahlen trenne.
Ich hoffe, das Beispiel hat einen kleinen Einblick in ein paar der Probleme
gegeben.
und entsprechend auch wieder zu lesen:
quelle >> z1 >> str >> z2;
ok.
// Öffnen als Textdatei
quelle.open("testdat.txt", ios::in|ios::out);
Hier liegt ein weiteres Problem. Du öffnest die Datei, wie der Kommentar
richtig beschreibt, als Textdatei, schreibst dann aber binäre Daten
hinein.
Beim Schreiben in eine Textdatei können aber Zeichen geändert werden. So
ist
es bei einigen Systemen üblich, dass '\n' durch die zwei Zeichen '\x0D'
'\x0A' ersetzt wird. Und das passiert auch, wenn man Binärdaten in eine
Textdatei schreibt. Das bringt natürlich die Daten in der Datei ein
bisschen
durcheinander.
Du hast recht. Ich muss dafür (also bei meiner Version, die nicht
funktioniert), den Binärmodus benutzen.
In einer Version, die nicht funktioniert, ist es eigentlich egal, wie man es
falsch macht ;-)
Selbst wenn du die Datei im Binärmodus öffnest und das Problem mit dem
Leerzeichen löst, hast du noch ein Problem mit dem Zeilenende. In der Datei
steht dann zwar nicht 0D 0A sondern nur 0A, aber immer noch ein Zeichen zu
viel. Natürlich lässt sich auch dieses Problem lösen, aber was ist einfacher
zu schreiben und zu verstehen?
quelle.read((char*) &z1, sizeof(z1));
quelle >> str;
quelle.ignore(1);
quelle.read((char*) &z2, sizeof(z2));
quelle.ignore(1);
oder
quelle >> z1 >> str >> z2;
HTH
Heinz
|
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Heinz Ozwirk Guest
|
Posted: Fri Feb 03, 2006 7:00 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
"Ernst Baumann" <carlox (AT) web (DOT) de> schrieb im Newsbeitrag
news:h7n6u1hkfvov2frjjp624fj470jk5bi397 (AT) 4ax (DOT) com...
| Quote: |
Ob du die Datei im Binärmodus öffnen musst oder nicht, hängt nicht davon
ab,
was ich meine, sondern was dein Datenformat vorschreibt.
Das Datenformat legt fest, wie man die Daten ablegt (also ob als
Ganzzahl, Zeichenkette, double, ...). Ist das richtig ?
|
Ja. Das Datenformat, oder besser das Dateiformat, bestimmt wie der Inhalt
einer Datei auszusehen hat. Dieses Datenformat hat normalerweise nichts
damit zu tun, wie die Daten im Programm aussehen.
| Quote: | Ob ich die Datei im Textmodus oder Binärmodus öffne, ist völlg egal
(habe ich ausprobiert, Textmodus bzw, Binärmodus betrifft nur, ob das
Newline-Zeichen als 2 Byte oder 1 Byte in die Datei abgelegt bzw. aus
der Datei gelesen wird)
|
Es kann auch noch andere Unterschiede geben. Auf manchen Systemen gibt es
z.B. besondere Zeichen, die das Ende des Textes kennzeichnen. Wenn man auf
so einem System eine Binärdatei, in der so ein EOF-Zeichen steht, im
Textmodus liest, bekommt man nur die Daten bis zu diesem Zeichen. MS-DOS hat
so auf ein Byte mit dem Wert 0x1A reagiert. Wie es heute unter Windows
aussieht, weiß ich nicht. Ich lese Dateien, wenn ich nicht ganz sicher bin,
dass sie wirklich nur Text enthalten, normalerweise binär und kümmere mich
erst später um die Bedeutung der einzelnen Bytes. Das soll jedoch auf keinem
Fall heißen, dass diese Dateien auch wirklich Binärdaten enthalten.
| Quote: | Man kann read, write, >>, << auch gemeinsam in Binärdateien als auch
in Textdateien verwenden. Der Programmierer muss dann eben mehr
denken.
|
Man muss aber nicht alles machen, nur weil man es machen kann. Und Denken
ist zwar eine sehr sinnvolle Tätigkeit, aber beim Programmieren gibt es
genug wichtiges, woran man denken sollte. Da schadet es nichts, wenn man
sich das Leben so leicht wie möglich macht.
| Quote: | Wenn man beides gleichzeitig in
einem Stream verwendet, heißt das, dass man in eine Datei gleichzeitig
Binärdaten und Textdaten schreibt. Die Datei ist dann also weder eine
Text-
noch eine Binärdatei.
Das kann doch in der Praxis vorkommen, oder sollte man das vermeiden ?
|
Natürlich kann in einer Binärdatei auch etwas stehen, was man dann als
String interpretiert, aber man tut dann ganz gut daran, so zu tun als ob es
Binärdateien sind. Nur weil eine Datei ein paar als solche identifizierbare
Wörter enthält, wird sie noch lange keine Textdatei. Inzwischen geht man
eher den Weg, möglichst viel als Text zu schreiben. Man spart sich dann
viele Probleme mit der Darstellung von Zahlen, Byte-Order und anderen
lästigen Kleinigkeiten. Nicht ohne Grund ist XML heute in aller Munde (wobei
manche den Mund auch etwas zu voll nehmen).
| Quote: | Übrigens:
Wann ist es sinnvoll Daten, z.B. Zahlen als Binärzahl abzulegen?
Man kann sie doch (wie du mir vorgeschlagen hast) als Zeichenkette
ablegen und dann wieder durch quelle << z1 als Zahl einlesen.
Wenn ich aber Zahlen als Zeichenketten ablegen kann, brauch ich auch
die Befehle read und write nicht mehr. (Ich will den von dir gemachten
Vorschalg "verallgemeiner", d.h. immer benutzen, oder wo hat er seine
Grenzen)
|
Gute Frage. Binärdateien haben dann ihre Berechtigung, wenn der Aufwand zum
entschlüsseln einer Textdatei zu groß wird, wenn man allzu eifrige Benutzer
daran hindern will, zu freizügig in den Dateien herum zu ändern oder wenn es
einfach darauf ankommt, große Mengen von (temporären) Daten, die nicht lange
gebraucht werden, mal schnell auszulagern. Dagegen haben Textdateien
Vorteile, wenn es nötig ist, Daten auf andere Systeme zu übertragen, sie
auch in anderen Programmen zu verarbeiten, oder auch in fünf Jahren noch
darauf zugreifen zu können. Dateien, deren Inhalt vielleicht noch erweitert
werden muss, sind auch gute Kandidaten für Textdateien. Außerdem kann man
sich den Inhalt von Textdateien leicht ansehen und auch mal etwas ändern.
| Quote: | Ich habe etwas mit gemischten Daten (Binär- und Text) experimentiert:
1) Daten nur als Zeichenketen schreiben:
// Schreibe Testdaten
z1= 110;
z2= 111;
quelle << z1 << ' ' << "01.02.2004" << ' ' << z2 << endl;
z1 = 112;
z2 = 113;
quelle << z1 << ' ' << "05.03.2004" << ' ' << z2 << endl;
}
// Dateizeiger auf Dateianfang setzen
quelle.seekg(0, ios::beg);
while(quelle >> z1 >> str >> z2){
cout << z1 << ' ' << str << ' ' << z2 << endl;
}
// Jetzt Fehlerreport
if (!quelle.eof()){
if (quelle.bad())
cout << "Es gab ein technisches Problem\n"; // (*)
else
cout << "Format-Fehler!\n";
}
a) Ist es sinnvoll, den Auslesevorgang so zu implementieren ?
b) Ist meine Fehlerverwaltung (Fehlerreport) so sinnvoll oder fehlt
noch etwas ?
|
Beim Lesen ist es meistens am sinnvollsten, immer ganze Zeilen zu lesen und
diese anschließend selbst zu parsen. Man kann dann schon besser auf Fehler
reagieren. Es ist ziemlich schwierig fehlerhafte Daten zu erkennen, wenn
fremder Code (in der Laufzeit Bibliothek) schon versucht hat, einen Fehler
irgendwie zu beheben. Was passiert z.B. in deinen Code, wenn eine Zeile
nicht mit einer Zahl beginnt? z1 behält dann den Wert, den es vorher schon
hatte. str enthält das, was eigentlich in z1 stehen sollte aber nicht
richtig konvertiert wurde, und z2 enthält vielleicht so viel von dem, was
eigentlich in str stehen sollte, wie das System konvertieren konnte. Und der
Rest wird im nächsten Durchgang durch die Schleife (vermutlich auch falsch)
verarbeitet.
Eine Fehlerbehandlung sollte meistens so früh wie möglich gemacht werden.
Das bedeutet u.a., dass auch die gelesenen Werte noch untersucht werden
sollten. Haben z1 und z2 plausieble Werte? Sieht der Ihalt von str so aus,
wie er aus sehen sollte (Tag.Monat.Jahr)? Dann bekommt man meistens bessere
Fehlermeldungen als "Format-Fehler" oder "Es gab ein technisches Problem".
Solche Fehlermeldungen sind ähnlich aussagekräftig wie "Geht nicht". Wie
würdest du es finden, wenn dein Compiler am Ende der Übersetzung einfach nur
sagt "Ich habe einen Syntax-Fehler gefunden"?
Auf bad und eof würde ich mich eher nicht verlassen. Auch wenn das Dateiende
erreicht wurde, heißt das noch nicht, dass die Daten auch wirklich in
Ordnung waren. Vielleicht fehlt ja noch etwas. Und wenn es ein technisches
Problem gab, ist es auch hilfreich, wenn man weiß, um welches Problem es
sich handelt. Ist die Netzwerk-Verbindung unterbrochen? Ist die Festplatte
defekt? Oder was ist passiert? Andererseits lesen viele Benutzer
Fehlermeldungen sowieso nicht. Also reicht in bei "technischen Problemen"
tatsächlich oft ein lapidarer Hinweis wie "Fehler beim Lesen der Datei
xyz" - mit einer Ausnahme: Wenn beim Schreiben die Platte voll ist, sollte
man es dem Benutzer sagen. Aber in letzter Instanz kann man dafür keine
einfachen Regeln aufstellen. Das hängt sehr vom Programm ab.
| Quote: | c) Was mir nicht ganz klar ist:
Im Textmodus wird beim Befehl
quelle.read(&tempchar, 1);
das Newline-Zeichen eingelesen. Dies sind im Textmodus aber 2 Byte.
In tempchar wird aber nur 1 Byte einegelsen. Das ist mir auch noch
klar. Aber es wird nicht das 1. Byte eingelesen, sondern das 2. Byte.
Das ist mir nicht klar.
Ich hätte erwartet, dass man im Textmodus zweimal den Befehl schreiben
muss, also:
quelle.read(&tempchar, 1);
quelle.read(&tempchar, 1);
um alle 2 Bytes des Newline-Zeichens aufzunehmen.
Warum ist das so ?
|
Beim Lesen im Textmodus versucht die Laufzeit Bibliothek so viel wie möglich
von dem Rückgängig zu machen, was beim Schreiben verändert wurde. Dazu
gehört auch, dass aus den zwei Zeichen am Zeilenende wieder nur eins gemacht
wird. Das passert bevor read die Zeichen zu sehen bekommt. Normalerweise
funktioniert das, wenn die Datei im gleichen Modus geschrieben und gelesen
wird.
Mir sind drei Varianten bekannt, wie der Textmodus arbeitet. Auf Unix&Co
gibt es keinen Unterschied zwischen Text- und Binärmodus. Auf den von CP/M
abstammenden Systemen (u.a. MS-DOS, OS/2 und Windows) wird beim Schreiben
Newline (0x0A) durch CRLF (0x0D, 0x0A) ersetzt und beim Lesen wird CR vor LF
herausgefiltert. (Was passiert, wenn man ein CR ohne LF oder ein LF ohne CR
findet, variiert.) Bei (älteren?) Macs werden CR und LF vertauscht. Man
schreibt LF und in der Datei steht CR und beim Lesen wird daraus wieder LF.
Problemeatisch wird es, wenn sie im Textmodus geschrieben und im Binärmodus
gelesen wird (oder umgekehrt). Und das ist leider kein seltenes Problem, da
man es einer Datei mit Binärdaten nicht ansieht wie sie geschrieben wurde.
Bei einer Textdatei ist es einfach. Wenn zwei Zeichen am Zeilenende stehen,
wurde sie auf jedem Fall im Textmodus geschrieben (oder sie wurde im
Binärmodus geschrieben und der Programmierer hat sich selbst die Mühe
gemacht, das "richtige" Zeilenende zu erzeugen). Wenn am Zeilenende jedoch
nur ein 0x0A steht, dann wurde die Datei entweder im Binärmodus geschrieben
oder sie stammt von einem System, das nur 0x0A als Zeilenende verwendet.
HTH
Heinz
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Sun Feb 05, 2006 11:02 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
| Quote: |
Man muss aber nicht alles machen, nur weil man es machen kann. Und Denken
ist zwar eine sehr sinnvolle Tätigkeit, aber beim Programmieren gibt es
genug wichtiges, woran man denken sollte. Da schadet es nichts, wenn man
sich das Leben so leicht wie möglich macht.
Dus hast recht, aber um die Funktionsweise zu verstehen, ist es |
wichtig ein paar Experimente und Test zu machen.
| Quote: |
Gute Frage. Binärdateien haben dann ihre Berechtigung, wenn der Aufwand zum
entschlüsseln einer Textdatei zu groß wird,
was meinst du mit entschlüsseln ? Kannst du mir ein konkretes Beispiel |
geben ?
| Quote: |
wenn man allzu eifrige Benutzer
daran hindern will, zu freizügig in den Dateien herum zu ändern oder wenn es
einfach darauf ankommt, große Mengen von (temporären) Daten, die nicht lange
gebraucht werden, mal schnell auszulagern.
Große Datenmengen (z.B. Zahlen) brauchen binär weiniger Speicher als |
wie wenn man sie als Text abspeichert. Hast du das gemeint ?
| Quote: |
Dagegen haben Textdateien
Vorteile, wenn es nötig ist, Daten auf andere Systeme zu übertragen,
Angenommen, man hat eine große Zahl (z.B. 100000) als Zeichenkette |
(z.B. mit quelle << z) abgespeichert. Nun will man auf einem anderen
Computer diese Zahl lesen, z.B. mit:
int z;
quelle >> z
wobei aber der Compiler für dieses Programm für die Zahl z nur 2 Byte
Speicher reserviert. Dann kann aber auf diesem System diese Zahl auch
nicht gelesen werden, weil die Zahl 100000 mehr als 2 Byte benötigt.
Warum beitet es dann Vorteile ("Daten auf andere Systeme zu übertragen
") Zahlen als Text abzuspeichern ?
| Quote: |
Beim Lesen ist es meistens am sinnvollsten, immer ganze Zeilen zu lesen und
Das habe ich doch gemacht: jeweils einen ganzen Datensatz (Zahl, |
Zeichenkette, Zahl) lesen.
| Quote: |
diese anschließend selbst zu parsen. Man kann dann schon besser auf Fehler
reagieren. Es ist ziemlich schwierig fehlerhafte Daten zu erkennen, wenn
fremder Code (in der Laufzeit Bibliothek) schon versucht hat,
Was meinst du damit ("fremder Code (in der Laufzeit Bibliothek) schon |
versucht hat") ?
Wo habe ich das gemacht ?
| Quote: |
einen Fehler
irgendwie zu beheben. Was passiert z.B. in deinen Code, wenn eine Zeile
nicht mit einer Zahl beginnt? z1 behält dann den Wert, den es vorher schon
hatte.
Den Fall gibt es nicht: Die z.B. 4 Bytes werden auf jeden Fall durch: |
quelle >> z
als Zahl interpretiert. Oder was meinst du damit ?
| Quote: |
str enthält das, was eigentlich in z1 stehen sollte aber nicht
richtig konvertiert wurde, und z2 enthält vielleicht so viel von dem, was
eigentlich in str stehen sollte, wie das System konvertieren konnte. Und der
Rest wird im nächsten Durchgang durch die Schleife (vermutlich auch falsch)
verarbeitet.
Eine Fehlerbehandlung sollte meistens so früh wie möglich gemacht werden.
Gut, aber ich meine das Schema, das ich benutzt habe: |
Lesen mit:
while(quelle >> z1 >> str >> z2){
bis ein Fehler auftritt.
Gut, man müsste noch weiter die gelesenen Werte untersuchen und dabei
bei einem Fehler den Lesevorgang abbrechnen.
In meinem Beispiel habe ich die Zahlen nur ausgegeben:
cout << z1 << ' ' << str << ' ' << z2 << endl;
Man hätte hier noch weitere Untersuchungen machen müssen.
Wie würdest du schematisch (programmtechnisch) den Einlesevorgang und
das Fehlermanagement skizzieren (so dass man es als Schema verwenden
kann) ??
| Quote: |
Beim Lesen im Textmodus versucht die Laufzeit Bibliothek so viel wie möglich
von dem Rückgängig zu machen, was beim Schreiben verändert wurde. Dazu
gehört auch, dass aus den zwei Zeichen am Zeilenende wieder nur eins gemacht
wird.
D.h. beim Schreiben (bei Windows) wird das 1 Byte große Zeichen '\n' |
(mit ASCII-Wert 10) als die (zusammen 2 Byte) zwei Zeichen (mit den
ASCII-Werten) 0D0A in die Datei abgespeichert.
Umgekehrt werden beim Lesen von der Datei die 2 Zeichen 0D0A als das
eine Zeichen 0A (ASCII-Wert 10) gelesen (durch den Befehl
quelle.read(&tempchar, 1))
Ist das so korrekt ?
mfg
Ernst
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
kanze Guest
|
Posted: Tue Feb 07, 2006 3:02 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
Heinz Ozwirk wrote:
[...]
| Quote: | Problemeatisch wird es, wenn sie im Textmodus geschrieben und
im Binärmodus gelesen wird (oder umgekehrt).
|
Problematisch wird es auch, wenn auf einem System geschrieben
wird, und auf einem anderen gelesen. In der Praxis benutze ich
häufig ios::binary auf Text-Dateien, weil ich selber die
Darstellung der Zeilenenden beherrschen will (oder muss).
(In Prinzip können viele andere Sachen in Text-Dateien anders
sein, wie z.B. die Zeichencodierung. In der Praxis, auf den
Rechnern, die mir interessieren, sind Zeilenden das einzige
Problem.)
--
James Kanze GABI Software
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
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Heinz Ozwirk Guest
|
Posted: Tue Feb 07, 2006 6:02 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
"Ernst Baumann" <carlox (AT) web (DOT) de> schrieb im Newsbeitrag
news:7ufcu1dega79crh2b4migfjk5kourmj0c3 (AT) 4ax (DOT) com...
| Quote: | wenn man allzu eifrige Benutzer
daran hindern will, zu freizügig in den Dateien herum zu ändern oder wenn
es
einfach darauf ankommt, große Mengen von (temporären) Daten, die nicht
lange
gebraucht werden, mal schnell auszulagern.
Große Datenmengen (z.B. Zahlen) brauchen binär weiniger Speicher als
wie wenn man sie als Text abspeichert. Hast du das gemeint ?
|
Nein. Zahlen brauchen binär nicht unbedingt weniger Speicher als in
Textdarstellung. Zahlen unter 1000 brauchen als Text höchstens 4 Zeichen,
als Text aber (auf aktuellen Systemen) meistens 4 oder 8 Bytes. Aber wenn es
darum geht z.B. 1 Million int's in einem Array mal schnell auszulagern und
vom gleichen Programm, übersetzt mit dem gleichen Compiler auf dem gleichen
Rechner wieder zu lesen, ist ein write(&array[0], sizeof(array) bzw. ein
entsprechendes read einfach schneller als jede Zahl einzeln in einen String
umzuwandeln und den String dann wieder mühsam in eine Zahl zurück zu
wandeln.
| Quote: | Angenommen, man hat eine große Zahl (z.B. 100000) als Zeichenkette
(z.B. mit quelle << z) abgespeichert. Nun will man auf einem anderen
Computer diese Zahl lesen, z.B. mit:
int z;
quelle >> z
wobei aber der Compiler für dieses Programm für die Zahl z nur 2 Byte
Speicher reserviert. Dann kann aber auf diesem System diese Zahl auch
nicht gelesen werden, weil die Zahl 100000 mehr als 2 Byte benötigt.
Warum beitet es dann Vorteile ("Daten auf andere Systeme zu übertragen
") Zahlen als Text abzuspeichern ?
|
Wenn die Zahlen so groß sind, dann reichen 2 Bytes halt nicht aus. Also muss
man 4 oder 8 nehmen. Wenn der Zielrechner das nicht kann, dann läuft da halt
nicht die richtige Software. Aber wenn man binäre Dateien auf so einen
Rechner transportiert, funktioniert es auch nicht. Der kann die 4 Byte des
Ursprungsrechners auch nicht auf 2 Bytes komprimieren.
| Quote: | Beim Lesen ist es meistens am sinnvollsten, immer ganze Zeilen zu lesen
und
Das habe ich doch gemacht: jeweils einen ganzen Datensatz (Zahl,
Zeichenkette, Zahl) lesen.
|
Die Input-Operatoren kümmern sich nicht um Zeilenenden. Für die ist ein
Zeilenende nur so etwas wie ein Wortzwischenraum. Wenn dein Programm Zeilen
mit 3 Zahlen lesen soll und in jeder Zeile 4 Zahlen stehen, dann liest es
erst die ersten drei Zahlen der ersten Zeile, dann die vierte Zahl der
ersten und die ersten beiden Zahlen der zweiten Zeile usw.
| Quote: | irgendwie zu beheben. Was passiert z.B. in deinen Code, wenn eine Zeile
nicht mit einer Zahl beginnt? z1 behält dann den Wert, den es vorher schon
hatte.
Den Fall gibt es nicht: Die z.B. 4 Bytes werden auf jeden Fall durch:
quelle >> z
als Zahl interpretiert. Oder was meinst du damit ?
|
quelle >> z überliest erst alle Leerzeichen (sowie Tabs und Zeilenenden) und
baut dann aus so vielen Ziffern wie möglich eine Zahl. Ob diese Zahl dann in
4 oder 42 Bytes gespeichert werden, ist erst einmal ohne Bedeutung. Die
Input-Operatoren richten sich nach den Zeichen, die sie finden, nicht nach
der Anzahl der Bytes in denen etwas gespeichert werden. Dadurch
unterscheiden sich die text-orientierten Funktionen und Operatoren von
denen, die binäre Daten lesen, ohne sich um deren Werte zu kümmern.
Heinz
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Wed Feb 08, 2006 7:10 pm Post subject: Re: Problem: VERSCHIEDENE Datentypen in einer Datei |
|
|
| Quote: |
Die Input-Operatoren kümmern sich nicht um Zeilenenden. Für die ist ein
Zeilenende nur so etwas wie ein Wortzwischenraum. Wenn dein Programm Zeilen
mit 3 Zahlen lesen soll und in jeder Zeile 4 Zahlen stehen, dann liest es
erst die ersten drei Zahlen der ersten Zeile, dann die vierte Zahl der
ersten und die ersten beiden Zahlen der zweiten Zeile usw.
..... |
.....
| Quote: |
quelle >> z überliest erst alle Leerzeichen (sowie Tabs und Zeilenenden) und
baut dann aus so vielen Ziffern wie möglich eine Zahl. Ob diese Zahl dann in
4 oder 42 Bytes gespeichert werden, ist erst einmal ohne Bedeutung. Die
Input-Operatoren richten sich nach den Zeichen, die sie finden, nicht nach
der Anzahl der Bytes in denen etwas gespeichert werden. Dadurch
unterscheiden sich die text-orientierten Funktionen und Operatoren von
denen, die binäre Daten lesen, ohne sich um deren Werte zu kümmern.
Kannst du mir ein Schema liefern, wie du mein Programm implementieren |
würdest ?
mfg
Ernst
--
de.comp.lang.iso-c++ - Moderation: mailto:voyager+mod (AT) bud (DOT) prima.de
FAQ: http://www.voyager.prima.de/cpp/ mailto:voyager+send-faq (AT) bud (DOT) prima.de |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|