Binäres Speichern / lesen einer Datei
-
Hi,
also ich muss mehrere verschiedene Dateiformate in eine einzelne Datei packen (z.B. Text und ein Bild). Aber, wenn ich den Inhalt einer Bilddatei als Text speichere, dann "funktioniert" diese neue Datei nicht mehr da ja der Text in die Ansi (oder in irgendeine andere Form) umgewandelt wird.
Allerdings ist es möglich (und das hab ich schon probiert) ein Bild als "Text" auszulesen und "binär" zu speichern. Das ergebnis war das das neue Bild von Paint usw. geöffnet werden konnte und auch nicht weiter defekt war (mal abgesehen davon das ich pauschal 50000 Zeichen für ein bild festgelegt habe und die am Ende entstehende Lücke durch Î ersetzt wurde).
Jetzt mache ich mir allerdings gedanken was mit diesem Binären schreiben passiert wenn sich die Rechnerarchitektur ändert. Also wenn die Leute irgendwann von 32 auf 64 Bit umsteigen. Bis jetzt packe ich den Bildinhalt als Text in ein
char text[50000];
-feld. Abar was passiert mit dem "char" wenn das Programm auf einer 64 Bit Architektur ausgeführt wird?
Der Befehl:fwrite(satz, (sizeof(char) ), 50000, ZuSpeicherndeDatei);
würde ja anders funktionieren oder? wenn ein normales char jetzt 4 Bit groß ist, dann muss es das aber nicht in 5 Jahren auch so sein
Oder kennt jemand eine bessere Methode verschiedene Dateitypen miteinander zu "verschmelzen"? Ich finde so einen Binären Block innerhalb einer Textdatei (die von aussen auch nicht lesbar sein wird) recht sauber, da ich nur den Block extrahieren muss um das ganze binär in eine Datei zu schreiben. Und voila, das Bild ist nicht defekt.
-
also ein char ist 8 bit groß, und das wird sich auch nirgends ändern.....
-
hi,
ich wollte darauf hinweisen, dass ein char 8 bit gross ist und nicht 4. Dies wird warscheinlich auch immer so bleiben, da der reguläre ASCII code 127 Zeichen gross ist. Mit 8 bit lassen sich da 256 verschidene Zeichen darstellen
(erweiterter Zeichensatz). Daher bin ich überzeugt, dass man eher einen neuen Datentyp einführen wird, statt dem char mehr als 1 Byte zu geben.ANSI C kennt den Datentyp "void". Ein zeiger dieses types zeigt auf Daten eines unbestimmten Datentypes.
So könntest du zB.... #define BILDSIZE 1024*1024 void *bildbuffer; bildbuffer= (void*)malloc(BILDSIZE); if(bildbuffer== NULL){ fprintf(stderr,"\nFehler bei malloc()\n"); exit(1); }
Jetzt hast Du nun genau einen Buffer der Grösse BILDSIZE, da malloc() immer als
Argument eine byteanzahl erwartet und nicht ein Vielfaches der zB chargroesse.Wenn du bei fwrite zB mit sizeof(int) arbeitest, so kann es tatsächlich bei verschidenen Systemen zu unterschieden kommen. So rate ich dir in dieser Situation immer zu einem void oder char-buffer.
gruss
-
Wenn du Daten binaer speichern willst, d.h. nichts anderes als jede Dateneinheit in Stücke von 1 Byte ≡ 8 Bit zu zurlegen, lont es scih vielleicht ein eigenes Datenformat zu definieren. Dieses Datenformat besteht aus zwei Teilen
Teil ein Header, Teil2 den Daten. Der Header ist mindestens ein Byte lang und definiert den Typ
Beispiel
HeaderByte in Hex
00 not used
01 Byte
02 int16
03 int32
04 int64
05 unsigned Byte
06 unsigned int16
07 insigned int32
08 unsigned int64
09 IEEE float 32 Bit
0A IEEE float 64 Bit
ab hier folgen dem Header 4 Byte und geben die Länge unsigned an Byte 1 Bit 31..24 Byte 2 Bit 33..16 Byte 3 Bit 15..8 Byte 4 Bit 7..0
10 Länge, string plus \0 somit kann ein C-string relativ einfach nach Pascal-Art mit fread bearbeitet werden, die Länge ist inclusive der \0
11 Länge, Binary Objectab hier folgen dem Header 8 Byte und geben die Länge unsigned an Byte 1 Bit 63..56 .. Byte 8 7..0
20 Länge, Binay large Objectab 30 frei eigene Formatdefinitionen, da lässen sich auch complexere Formate abbilden.
30 Uint32 Größe Bild, Uint32 Größe X, Uint32 Größe y, BinaerdatenDas Lesen würde dann mit
fopen(..,"rb"); char Typ; int Int32Val; vhar Buffer[10000]; fread(&Typ,sizeof(Byte),1,..) switch(Typ) {... case 0x03: fread(&Int32Val,sizeof(Byte),4,..) break; .... case 10: fread(&Int32Val,sizeof(Byte),4,..) fread(Buffer,sizeof(Byte),Int32Val,..) break; case 30: fread(&Int32Val,sizeof(Byte),4,..) fread(&Xsize,sizeof(Byte),4,..) fread(&Ysize,sizeof(Byte),4,..) fread(Buffer,sizeof(Byte),Int32Val,..) break; ...
Das schreiben analog dazu, mit fwrite, zuerst den Typ, dann die Länge, dann die Daten
So ein Verfahren hat uns sehr geholfen bei einer Meßdatenerfassung bei der viele Binaerdaten aber auch stings und Einzelwerte anfielen. Die Reihenfolge der Daten war zufällig und der Speicherplatz begrenzt.
Vielleicht hilfts
-
loki1985 schrieb:
also ein char ist 8 bit groß, und das wird sich auch nirgends ändern.....
hm... mein SE Prof meint das man mit den Datentypen und mit
sizeof()
vorsichtig sein soll da sich die größe auf unterschiedlichen Systemen ändern kann. Vieleicht meinte er auch "Betriebssysteme"... aber er erzählte auch was von "genormten" Datentypen die überall gleichgroß sind. z.B.
size_t
Leider hat sich mein Prof in der richtung sehr Sparsam Ausgedrückt.
PAD schrieb:
Wenn du Daten binaer speichern willst, d.h. nichts anderes als jede Dateneinheit in Stücke von 1 Byte ≡ 8 Bit zu zurlegen, lont es scih vielleicht ein eigenes Datenformat zu definieren. Dieses Datenformat besteht aus zwei Teilen
Teil ein Header, Teil2 den Daten. Der Header ist mindestens ein Byte lang und definiert den Typ
Beispiel
HeaderByte in Hex
00 not used
01 Byte
02 int16
03 int32
04 int64
05 unsigned Byte
06 unsigned int16
07 insigned int32
08 unsigned int64
09 IEEE float 32 Bit
0A IEEE float 64 Bit
ab hier folgen dem Header 4 Byte und geben die Länge unsigned an Byte 1 Bit 31..24 Byte 2 Bit 33..16 Byte 3 Bit 15..8 Byte 4 Bit 7..0
10 Länge, string plus \0 somit kann ein C-string relativ einfach nach Pascal-Art mit fread bearbeitet werden, die Länge ist inclusive der \0
11 Länge, Binary Object[...]
das ist ein sehr interesanter Ansatz. Allerdings kenne ich den Datentyp nicht. Wenn ich z.B. eine Pdf in meine Textdatei einbauen will, dann weis ich nicht was in der PDF nun Text oder Bild ist.
linu(x)bie schrieb:
ANSI C kennt den Datentyp "void". Ein zeiger dieses types zeigt auf Daten eines unbestimmten Datentypes.
So könntest du zB.... #define BILDSIZE 1024*1024 void *bildbuffer; bildbuffer= (void*)malloc(BILDSIZE); if(bildbuffer== NULL){ fprintf(stderr,"\nFehler bei malloc()\n"); exit(1); }
Jetzt hast Du nun genau einen Buffer der Grösse BILDSIZE, da malloc() immer als
Argument eine byteanzahl erwartet und nicht ein Vielfaches der zB chargroesse.Wenn du bei fwrite zB mit sizeof(int) arbeitest, so kann es tatsächlich bei verschidenen Systemen zu unterschieden kommen. So rate ich dir in dieser Situation immer zu einem void oder char-buffer.
auch eine tolle Idee, aber leider kenne ich die größe des Bildes (oder der Datei in Byte) nicht. gibts da vieleicht einen Befehl? Ich könnte zwar tricksen und einfach MFC Klassen mitbenutzen... aber das halte ich nicht für sauber. Und ich kenne auch keine MFC File Klasse die das binäre lesen und schreiben unterstützt.
PS: danke für eure Ideen
-
Ich will noch mal versuchen auf deine Frage einzugehen.
So wie ich das jetzt verstanden habe möchtest Du 2 verschiedene Dateien in einer ablegen. Dabei sollen die Dateien aber verschiedene Formate haben.
Ist das so richtig?Wenn ja so würd ich da machen:
1.)Binäres lesen mit fread() ist immer gültig, unabhängig vom Dateityp, da es die bits einfach so ausliest wie sie tatsächlich drin stehen. Also immer binär lesen auch bei Textdateien.
2.)Das gleiche gilt für's Schreiben. Das was Du ausgelesen hast, schreibst Du genauso wider in die neue Datei (fwrite).
3.)Ich würde nicht erst die komplette Dateien in einen Buffer schreiben und erst dann in die neue Datei, sondern stückweise.#define DATEI1 "datei.txt" #define DATEI2 "datei.bmp" #define OUTPUT "output.dat" #define BUFFERSIZE 1024 char lesebuffer[BUFFERSIZE]; ... while(fread(lesebuffer, BUFFERSIZE, 1, inputfd_1)> 0){ if(fwrite(lesebuffer, BUFFERSIZE, 1, outputfd)== BUFFERSIZE){ fprintf(stderr,"\nFehler bei fwrite()\n"); exit(1); } } while(fread(lesebuffer, BUFFERSIZE, 1, inputfd_2)> 0){ if(fwrite(lesebuffer, BUFFERSIZE, 1, outputfd)== BUFFERSIZE){ fprintf(stderr,"\nFehler bei fwrite()\n"); exit(1); } } ...
So musst Du dich nicht mehr um irgendwelche Buffergroessen kümmern.
Falls Du dennoch über einen Buffer gehen müchtest, kannst Du mit der Methode die Ich im vorherigen Post beschrieben habe mit malloc() Dieser schaft dir einen Buffer zur Laufzeit(!). Die Buffergroesse könntest Du den Dateigroessen ermitteln.Übrigens: Datentypen wie size_t sind von Ihrer tatsächlichen/physikalischen groesse/beschafenheit nicht definiert. Unter dem einem System kann das so sein und unter einem anderen anders. Dies ist es gerade was man erreichen will: Der Programmierer Braucht sich darum nicht zu kümmern. Er benutzt einfach den Datentypen size_t und weis nicht Unbedingt was es ist (braucht er ja nicht).
So bleibt der code auf andere Systeme portionierbar, obwohl die Datentypen phsikalisch anders sein können.Viel Erfolg
PS:code nicht auf fehler überprüft!
-
Dann schau die mal die Type 11 und 20 an
du nimmst deine Pdf Datei bestimmst die länge und legst dann ein Object typ 11 oder 20 an
oder vielleicht etwas geschickterDu beginnst mit einem Object Typ 10 mit dem Inhalt filename der pdf.datei und fügst dann eine Object 11 oder 20 mit dem Inhalt an
oder wenn du immer öfter solche Dateein hast definier dir einen Typ mit folgenden ElementenTyp 31 besteht aus
int16 in der wird der Dateityp codiert 01 ≡ pdf 02 ≡ bmp 03≡mp3 04 ≡jpg
int16 länge des folgenden Strings
.... Bytes mit dem filenamen (genauso wie Typ 10)
int32 länge der Datei
..... Bytes mit dem Inhalt der datei@linu(x)bie Ich habe ihn nicht so vberstanden dass er die Dateien einfach so hintereinanderkopieren will, denn das geht ja ganz
simpel mit copy datei1+datei2+datei3 datei4. ich hab das so verstanden das er den Inhalt von mehreren Dateien zusammenführen möcht und später auch wieder an die einzelnen Elemente herankommen möchte.
-
PAD schrieb:
@linu(x)bie Ich habe ihn nicht so vberstanden dass er die Dateien einfach so hintereinanderkopieren will, denn das geht ja ganz
simpel mit copy datei1+datei2+datei3 datei4. ich hab das so verstanden das er den Inhalt von mehreren Dateien zusammenführen möcht und später auch wieder an die einzelnen Elemente herankommen möchte.achso. Stimmt, dann ist header unentberlich. Dein Ansatz find ich tatsächlich nicht schlecht
mfg
-
Der Ansatz hat aber auch noch seine Probleme,
- wenn einmal die Synchronisation weg ist, findet man sie nicht wieder
- wenn in einer Datei viele objekte gespoeichert sind muß für random access jedes man sequentiell von vorne die Datei bis zum Ziel lesen.
- ein Systemabsturz kann diese Datei und somit alle Members unwiederbringlich zerstören- Persönlich würde ich so was für Messdaten machen, bei pdf, mp3, jpg, bmp, png
Dateien würde ich an Stelle der einzelnen Datei eine Directory aufmachen und die Dateien in dieser speichern. vielleicht käme noch eine Index Datei ASCII hinzu, in der ich eine definierte Reihenfolge des logischen Ablauf´s dieser Dateien hinterlege. Warum soll man manche Räder neu erfinden. Dies hätte auch den Vorteil, das bei einem crash der irgendeine Datei beschädigt, zumindest alle anderen Daten noch da wären.
-
PAD schrieb:
Der Ansatz hat aber auch noch seine Probleme,
- wenn einmal die Synchronisation weg ist, findet man sie nicht wiederWas meinst du mit Synchronisation?
PAD schrieb:
- ein Systemabsturz kann diese Datei und somit alle Members unwiederbringlich zerstören
Das muss ich mal testen. Wäre fatal wenn mein Programm grad die Dateien "kopiert" und alles wäre nach einem Absturz futsch. Werden denn die (Physikalischen) Dateien "geleert" wenn man eine mit "fopen" öffnet? Normalerweise dürfte das nicht so sein. Ich meine, ich kann ja auch mit Notepad eine Datei öffnen und wenn mein Rechner dann abstürzt ist die Datei immer noch das (und unbeschädigt).
PAD schrieb:
- Persönlich würde ich so was für Messdaten machen, bei pdf, mp3, jpg, bmp, png
Dateien würde ich an Stelle der einzelnen Datei eine Directory aufmachen und die Dateien in dieser speichern. vielleicht käme noch eine Index Datei ASCII hinzu, in der ich eine definierte Reihenfolge des logischen Ablauf´s dieser Dateien hinterlege. Warum soll man manche Räder neu erfinden. Dies hätte auch den Vorteil, das bei einem crash der irgendeine Datei beschädigt, zumindest alle anderen Daten noch da wären.Naja ich soll so eine Art "Export-" Funktion schreiben. Da soll der Nutzer am ende eine Datei erhalten die er überall hinkopieren könnte. Das mit dem Verzeichnis erstellen passt da nicht so, da er ja dann auch einfach das Programm verzeichnis kopieren könnte. (davor will ich ihn ja abhalten)
Es geht mir bei diesem Problem auch nicht um den Programmiertechnischen Aufwand, sondern eher um die Denkweise... also ob meine Idee mit dem Binären kopieren und lesen "sinnvoll" und sauber ist. Weis nicht, vieleicht gibts noch andere Möglichkeiten so einen Export zu realisieren bei dem Verschiedene Dateitypen in einer eingebunden werden.
-
Synchronisation:
Im Header eines Datensatze steht ja der Typ und die Länge, nach dem Header stehen die Daten. wenn ich es fertig bringe, das ich nach dem lesen der Daten nicht unmittelbar vor einem neuen Header stehe kann ich alle Daten danach nicht mehr sinnvoll zugreifen. das meine ich mit Synchronisation.Systemabsturz, deine Festplatte hat einen Fehler, ein anderes Programm schreibt durch einen Fehler in diese Datei, durch einen Systemabsturz werden auf der Platte crosslinked files erzeugt. D.h. wenn ein Byte aus einem Header verändert wird sind wegen den fehlenden Synchronisation die Daten danach nicht mehr lesebar.
Du kennst ja auch sicher Zip/rar-Archive, die du nach dem heruinterladen nicht mehr lesen kannst wegen einem Fehler in den Daten. Das gleiche gilt für deine Datei. Denn im Endeffekt machst du nichts anderes als als eine Zip-Datei ohne komprimierung zu erstellen.Da fällt mir gerade noch eine Unter Unix gibt es tar-Files, in denen werden mehrere Darteien in einem File gespeichert. Dieses Format ist auch unter Windows zugänglich. D.h. du könntest deine Daten aus deinem Programm heraus in eine tar-archiv verpacken. Beim Kunden wird dann aus dem tar-archiv die jeweils benötigte Datei von deinem Programm entnommen.
Um bei vielen Daten gut zurechtzukommen lohnt es sich vielleicht nach dem die Daten geschrieben sind an das Ende der Datei, ein Inhaltsverzeichnis mit den Startpositionen der Objekte anzuhängen.