Datei einlesen + Bytereihenfolge (Ganzzahlen)

tedlemegba

abgemeldet
20 April 2006
2.729
175
Ahoi zusammen!

Ich habe ein dateiformat- und programmiersprachenbedingtes Problemchen. Vielleicht ist es bislang auch nur ein gedankliches Problem.

Und zwar habe ich eine grooooooooooße Datei (MB im zweistelligen Bereich, könnte aber auch noch größer werden) in der mehrere Bytes gespeichert sind. :mrgreen: (Neeein, wer hätte es gedacht!)

16 Bit sind die einzelnen Werte jeweils lang und zwar eine "vorzeichenbehaftete Ganzzahl" (also zwischen +32.768 und −32.768 ... oder so ähnlich... bin da eh leicht verwirrt wegen der netten Zweierkomplementdarstellung nachdem ich vorher zum Glück noch nie mit negativen Ganzzahlen zu tun hatte... die obere und/oder untere Grenze möge also um 1 abweichen). Die hinzukommende Genialität: die Bytereihenfolge ist Little Endian (ja mit solchen Dingen musste ich mich vorher auch noch nie rumschlagen... ich bin ein Kind der Strings... Dateien musste ich so noch nicht auslesen).

Nun meine Frage/ mein Problem: wenn ich jetzt mit einer Programmiersprache arbeite -- (wie) kann ich das möglichst einfach einlesen und auch dirket als eine "vorzeichenbehaftete Ganzzahl" nutzen (um Beispielsweise Vergleiche mit Konstanten und Variablen oder andere Operationen vorzunehmen)? Kann ich das z.B. mit C? Interpretierte Sprachen sind nicht allzu sehr gefragt -- oder ist da kein großer Unterschied in Sachen Effizienz?

Bin euch schon mal sehr dankbar. Die Verwirrung möge klein sein!
 
Wenn du mit C arbeitest, interpretiert der C-Compiler entsprechend des Datentyps und der Bytereihenfolge der Maschine. Du musst dir keine Gedanken machen, ob bei
PHP:
int foo = 42;
nun 00 00 00 2A oder 2A 00 00 00 im Speicher steht.

Interessant wird es erst, wenn du böse Sachen machst:
PHP:
int foo = 42;
char* bar = (char*)&foo;
printf("%X", bar[0]);
kann dir 2A liefern oder aber auch 00 liefern, je nachdem wie die Bytereihenfolge ist.
(War bei mir ne Prüfungsaufgabe, wie ich in Assembler eine Funktion schreiben kann, die die Bytereihenfolge rauskriegt. So funktionierts in C :biggrin:)

Um die Verwirrung möglichst klein zu halten:
Mach nichts Böses, dann braucht dich Bytereihenfolge nicht zu interessieren. Es funktioniert auch alles, ohne dass du weißt, wie es funktioniert.
 
Um die Verwirrung möglichst klein zu halten:
Mach nichts Böses, dann braucht dich Bytereihenfolge nicht zu interessieren. Es funktioniert auch alles, ohne dass du weißt, wie es funktioniert.

Haaaaaaalt! Doch... ich will eine Datei einlesen mit dieser bösen Formatierung. Und ich will, dass die zwei Bytes richtig "eingelesen" und "interpretiert" werden. (Mit Sicherheit falsche Begriffe, aber ich will im Endeffekt die richtigen Werte dann in einer Variable haben mit der ich rumspielen kann!) Aber danke soweit. :biggrin:

Edit:
z.B. 0x00 0x80 (einlesen) bzw. den String (.€ [Whitespace, bla... wayne])
und als 0x8000 bzw. 0d+32768 damit arbeiten (ja, das ist das Gleiche). Also ohne da möglichst viel rumfummeln zu müssen mit Bytes vertauschen und Bits umkehren, etc.
 
Haaaaaaalt! Doch... ich will eine Datei einlesen mit dieser bösen Formatierung. Und ich will, dass die zwei Bytes richtig "eingelesen" und "interpretiert" werden.
Bist du sicher, dass du das willst ? Wenn da einer wirklich 32768 reingeschrieben hat und du ihn auf einer Maschine mit der selben Byteorder auslesen willst, dann kannst du das ohne böses Spielchen machen.

Ich speicher die 32768 ab
PHP:
int foo = 32768;
fwrite(&foo, sizeof(int), 1, fp);
und du liest sie
PHP:
int bar;
fread(&bar, sizeof(int), 1, fp);
Byteorder interessiert hier nicht.

Du musst dich nur damit auseinander setzen, wenn du zwischen Maschinen mit verschiedener Byteorder was austauschen willst. Da musst du irgendwie ein Kennzeichen haben, wie rum du lesen muss. Für dich als Programm ist die Zahl 32768 (00 00 80 00) ja genau dieselbe Zahl 8388608 (00 00 80 00) nur eben auf der anderen Maschine. Jede glaubt, die 00 00 80 00 richtigrum zu lesen.

UTF-16 z.B. löst es durch ein Zeichen, was verkehrtherum ungültig ist.
Zur Unterscheidung dieser Kodierungen wird empfohlen, das Unicode-Zeichen U+FEFF (BOM, byte order mark), das für ein Leerzeichen mit Breite Null und ohne Zeilenumbruch steht, an den Anfang des Datenstroms zu setzen. Wird dieses als U+FFFE – welches ein ungültiges Unicode-Zeichen ist – empfangen, dann bedeutet dies, dass die Bytereihenfolge zwischen Sender und Empfänger unterschiedlich ist und somit beim Empfänger die Bytes jedes 16-Bit-Wortes vertauscht werden müssen.
Quelle: https://de.wikipedia.org/wiki/UTF-16
 
Ich glaub du verstehst mich noch nicht so ganz.. und/oder ich dich nicht. Bytereihenfolge ist vom Dateiformat festgelegt! Und dass es mehrere Folgen von Werten zu je zwei Bytes sind ist auch festgelegt. Ja, ich will/muss diese Datei verarbeiten! :D

Allerdings gibt's ja dann noch die tolle Sache, dass die Datentypen von den Programmiersprachen unterschiedlich belegt sind. Also die Länge.. ich denke das sprichst du am Rande auch mit sizeof(int) an.

Das mit den Maschinen ist mir schon klar. Aber... kann man nicht so schreiben, dass für beide Systeme compiliert werden kann? Oder braucht's das nicht? *verwirrt*
 
Ich glaub du verstehst mich noch nicht so ganz.. und/oder ich dich nicht. Bytereihenfolge ist vom Dateiformat festgelegt!
Du verstehst mich nicht bzw. bist leicht falsch :yes:
Die Bytereihenfolge wird von der CPU vorgegeben ! Das is wie die Maschine intern arbeitet.
Da gibts kein Dateiformat, sondern nur Hardware auf dieser Ebene.

Also bitte vergess sofort das Wort "Byte-Order". Dieses Wissen brauchst du definitiv nicht, um ne Datei auszulesen.
Und dass es mehrere Folgen von Werten zu je zwei Bytes sind ist auch festgelegt. Ja, ich will/muss diese Datei verarbeiten! :D
Hierzu gehst du nach obigem Beispiel vor und liest soviele Bytes auf einmal, wie du lesen musst.
Allerdings gibt's ja dann noch die tolle Sache, dass die Datentypen von den Programmiersprachen unterschiedlich belegt sind. Also die Länge.. ich denke das sprichst du am Rande auch mit sizeof(int) an.
Der int ist systemabhängig, weil der immer der Daten-Breite des Prozessors entspricht.
Wie ich großgeworden bin, war ein int noch ein 2-Byte-Datentyp. Gestern war er ein 4-Byte-Datentyp und morgen wird er wohl die 8 Bytes kriegen.
Das mit den Maschinen ist mir schon klar. Aber... kann man nicht so schreiben, dass für beide Systeme compiliert werden kann? Oder braucht's das nicht? *verwirrt*
Du verwendest den Typ, den das Programm, was die Datei speichert, auch benutzt.
1 Byte = char
2 Byte = short
4 Byte = long

Ob unsigend oder signed is ja wurscht, die Daten sind dieselbe; is ja nur Interpretationssache mit dem Vorzeichen.
 
Du verstehst mich nicht bzw. bist leicht falsch :yes:
Die Bytereihenfolge wird von der CPU vorgegeben ! Das is wie die Maschine intern arbeitet.
Da gibts kein Dateiformat, sondern nur Hardware auf dieser Ebene.
Autsch. Jetzt hab ich's gleich. Ich wurde praktisch durch meinen Hex-Editor in die falsche Richtung gelenkt.. in dem ich die Bytereihenfolge in der entsprechenden Reihenfolge meines Systems gesehen habe? Bin trotzdem noch verwirrt.. weil das Dateisformat so beschrieben wird. Kann dir ja evtl. mal eben 'ne PN schicken? :mrgreen:

Hierzu gehst du nach obigem Beispiel vor und liest soviele Bytes auf einmal, wie du lesen musst.
Na ja.. ich brauch eh alle.. also praktisch Zweierpäärchen in 'ner großen Schleife.

Du verwendest den Typ, den das Programm, was die Datei speichert, auch benutzt.
1 Byte = char
2 Byte = short
4 Byte = long
Dann nehme ich ein playte! :ugly:

Ob unsigend oder signed is ja wurscht, die Daten sind dieselbe; is ja nur Interpretationssache mit dem Vorzeichen.
Na ja.. wenn ich Zahlen miteinander vergleiche nicht wirklich, oder?

Schon mal einen großen zweiten oder dritten Dank! :D
 
Ich wurde praktisch durch meinen Hex-Editor in die falsche Richtung gelenkt.. in dem ich die Bytereihenfolge in der entsprechenden Reihenfolge meines Systems gesehen habe?
Richtig. Der Hex-Editor zeigt nämlich immer - ans Beispiel von oben anknüpf - 00 00 80 00, du weißt dann aber noch nicht, obs im Programm nun als 32768 oder 8388608 gelesen wird.
Kann dir ja evtl. mal eben 'ne PN schicken? :mrgreen:
Wenns keine codierten P*rno-Links sind, kannst du es sogar hier posten :p
Na ja.. wenn ich Zahlen miteinander vergleiche nicht wirklich, oder?
Da weist dich der Compiler schon draufhin, dass -1 < 4, aber 4294967295 > 4.
Fürs Lesen/Schreiben von Daten ist das aber wurscht. Du brauchst signed/unsigned erst, wenns um die Logik im Programm geht.
Schon mal einen großen zweiten oder dritten Dank! :D
Bedank dich immer erst, wenn du die Rechnung bekommen hast :D :ugly:
 
Also bitte vergess sofort das Wort "Byte-Order". Dieses Wissen brauchst du definitiv nicht, um ne Datei auszulesen.

Diese Behauptung wäre schon fast einen roten Popel wert :evil:

Dieses Wissen braucht man mitunter sehr wohl: Wenn beispielsweise im WAV-Format definiert ist, daß nach 46 Bytes (wenn ich mich recht erinnere) Header, in dem Abtastrate etc. definiert werden eine Menge von Amplitudenwerten als vorzeichenbehaftete 16-Bit-Werte im Little-Endian-Format steht, dann müßtest Du auf einem Power PC (der ja im Big-Endian Format arbeitet) nach dem Einlesen die Bytes jeweils paarweise vertauschen. Der dezimale Wert "8" Stünde in der Datei als 08 00 und ein alter Mac würde ohne Vorsorge daraus 0800 machen statt 0008. Es ist aber ein wichtiger Unterschied, ob da 8192 oder 8 als Amplitude steht.

Ich gehe aber mal davon aus, daß Happymaster auf einer Intel-Maschine unter Windows arbeitet und nicht auf einer PowerPC-basierten Maschine. In diesem speziellen Fall braucht er dann wirklich nicht weiter darüber nachdenken.
 
Harr.. es treibt mich noch zur Verzweiflung.
Ich hab mich jetzt mal in C versucht.

Code:
[FONT="Courier New"]
#include <stdio.h>
#include <errno.h>

int main()
{
  FILE *handle;
  char filename[] = "./rawaudiofile";
  unsigned char ch, byte1, byte2;
  unsigned int i, j, k;

  handle = fopen (filename, "rb");

  i = 0; k = 0;
  while (1)
  {
    if(i == 0) {
      byte1 = fgetc(handle);
    }
    if(i == 1) {
      byte2 = fgetc(handle);
      //j = byte2*256 + byte1 - 32768;
      printf("%2X %2X   ", byte2, byte1);
    }

    if(k % 12 == 0) {
      printf("\n");
    }

    i++; k++;
    i = i % 2;

    if ( k > 20000 ) {
      printf("Ende\n");
      return;
    }
  }
  fclose (handle);

  return 0;
}[/FONT]

Man muss es jetzt nicht verstehen... aber die Bytereihenfolge ist doch nichts Unwesentliches, wenn ich die aus einer Datei einlese. Dann steht da doch z.B. einfach "B" vor "A" (wenn man jetzt mal ein Byte als ASCII-Zeichen interpretiert). Die Reihenfolge ist doch festgelegt!

Dooferweise geht's in der Tat um RIFF WAVE, wie dubberle schon anmerkte. (Der Länge des Headers kann übrigens variieren, aber das ist für mich erstmal unwichtig. Mir geht's um den data-chunk.)

Wie verklickere ich denn C, dass ich jetzt eine 16 Bit "vorzeichenbehaftete Ganzzahl" (signed) einlese? Schätzungsweise gar nicht, oder? Ich kann auch nur chars oder einen längeren String einlesen, oder? Oder gibt's noch andere Möglichkeiten. Habe nicht wirklich Erfahrungen in C bisher. Klappt's dann auch mit dem lieben Endian? Ich bin da immer noch total verunsichert.
 
char c1 = fgetc(handle);
char c2 = fgetc(handle);
signed short s = c1 + ((signed short) c2) << 8 );


Nicht die kürzeste aber die übersichtlichste Variante.
 
Zuletzt bearbeitet:
char c1 = fgetc(handle);
char c2 = fgetc(handle);
signed short s = c1 + ((signed short) c2) << 8);


Nicht die kürzeste aber die übersichtlichste Variante.

Geilo und das geht? *ausprobieren geh*
Vielen dank dir! :]

Edit: Muha.. da seh ich grad wie sinnlos der Schleifendurchlauf mit meiner Zählervariable ist.. (aber das war dadurch gegeben, dass in der Bedingung der while-Schleife mal was von EOF drin stand... das musste gezwungenermaßen raus). So kann man in einem Schleifendurchlauf natürlich gleich zwei Stück (chars) einlesen. Schön, ja.. und logisch! :D

Wenn du's jetzt noch erklären könntest. Könnte eine Bit-Verschiebung sein? Sprich das Gleiche wie *256, was ich eigtl. vorgesehen hätte?

Edit: Hat noch eine Klammer gefehlt.. aber sieht gut aus.

Code:
[FONT="Courier New"]#include <stdio.h>
#include <errno.h>

int main()
{
  FILE *handle;
  char filename[] = "./rawaudiofile";
  unsigned int k;

  handle = fopen (filename, "rb");

  k = 0;
  while (1)
  {
    char c1 = fgetc(handle);
    char c2 = fgetc(handle);
    signed short s = c1 + (((signed short) c2) << 8);

    printf("%d\n", s);
    
    if ( k > 20000 ) {
      printf("Ende\n");
      break;
    }

    k++;
  }
  fclose (handle);

  return 0;
}[/FONT]
(Ja, bislang wäre eine for-Schleife mit Sicherheit nützlicher. ^^)

Code:
[FONT="Courier New"]...
5511
4749
5445
4104
3110
2562
2939
1775
1878
938
-328
-802
-861
-503
-52
-2133
-1933
-887
-1515
-2265
...
Ende[/FONT]

Edit: Wenn wir schon "dabei" sind.. kennt ihr eine gute Entwicklungsumgebung/ Compiler für C unter Windows? Hab mir Bloodshed Dev-C++ geholt... aber mit dem Editor komm ich nicht so richtig zurecht. (keine Zeilennummern, keine Whitespaces angezeigt, kein Indent, ...)
 
Zuletzt bearbeitet:
x << 8 ist dasselbe wie x * 256.

Der Unterschied liegt in der Schwierigkeit für den Compiler den Code zu optimieren. x << 8 ist eine einziger Maschinenbefehl, x*255 wäre ein komplizierterer. Der Compiler müßte also bemerken, daß 255 keine höhere Potenz von 2 ist und ein normale Operation erfordert, während 256 zufälligerweise durch einen Linksshift zu bewältigen ist. Da du die Bitmuster verschoben anordnen willst ist x << 8 außerdem lesbarer. Noch besser als das Plus (von der Lesbarkeit her, vielleicht auch von der Optimierung her) wäre eine Oder-Verknüpfung:

signed short s = ((signed short) c2) << 8 | c1;