Frage

Ich arbeite auf ein Dateiformat, das geschrieben werden soll und in mehreren verschiedenen Betriebssystemen und Computern lesen. Einige dieser Computer sollten x86-Rechner sein, andere x86-64. Einige andere Prozessoren vorhanden sind, aber ich bin nicht besorgt über sie noch .

Dieses Dateiformat sollte mehrere Nummern enthalten, die wie folgt lesen würde:

struct LongAsChars{
    char c1, c2, c3, c4;
};

long readLong(FILE* file){
    int b1 = fgetc(file);
    int b2 = fgetc(file);
    int b3 = fgetc(file);
    int b4 = fgetc(file);
    if(b1<0||b2<0||b3<0||b4<0){
        //throwError
    }

    LongAsChars lng;
    lng.c1 = (char) b1;
    lng.c2 = (char) b2;
    lng.c3 = (char) b3;
    lng.c4 = (char) b4;

    long* value = (long*) &lng;

    return *value;
}

und geschrieben wie:

void writeLong(long x, FILE* f){
    long* xptr = &x;
    LongAsChars* lng = (LongAsChars*) xptr;
    fputc(lng->c1, f);
    fputc(lng->c2, f);
    fputc(lng->c3, f);
    fputc(lng->c4, f);
}

Obwohl dies auf meinem Computer zu funktionieren scheint, ich bin besorgt darüber, dass es in anderen nicht oder dass das Dateiformat anders kann am Ende über Computer (32 Bit vs 64 Bit-Computern, zum Beispiel). Mache ich etwas falsch? Wie soll ich meinen Code implementieren eine konstante Anzahl von Bytes pro Nummer verwenden?

Soll ich fread nur (was möglicherweise meinen Code schneller machen würde) statt?

War es hilfreich?

Lösung

Mit den Typen in stdint.h um sicherzustellen, dass Sie die gleiche Anzahl von Bytes in der und aus bekommen.

Dann sind Sie nur mit Endian Probleme mit Umgang links, die Sie wahrscheinlich Code nicht wirklich behandeln.

Serialisierung des lang mit einem aliased char * läßt Sie mit verschiedenen Bytereihenfolgen in der schriftlichen Datei für Plattformen mit unterschiedlichen Endianess.

Sie sollten die Bytes so etwas wie so zersetzen:

char c1 = (val >>  0) & 0xff;
char c2 = (val >>  8) & 0xff;
char c3 = (val >> 16) & 0xff;
char c4 = (val >> 24) & 0xff;

Und recompose dann etwas mit wie:

val = (c4 << 24) |
      (c3 << 16) |
      (c2 <<  8) |
      (c1 <<  0);

Andere Tipps

Sie können auch auf Probleme stoßen mit endianness . Warum nicht einfach so etwas wie verwenden NetCDF oder HDF , die aus irgendwelchen Portabilität Probleme kümmern, die entstehen können?

Anstatt mit Strukturen mit Zeichen in ihnen, sollten Sie einen mathematischen Ansatz:

long l  = fgetc() << 24;
     l |= fgetc() << 16;
     l |= fgetc() <<  8;
     l |= fgetc() <<  0;

Dies ist ein wenig direkter und klar, was Sie versuchen zu erreichen. Es kann auch in einer Schleife durchgeführt werden, um größere Zahlen zu behandeln.

Sie wollen nicht long int verwenden. Das kann verschiedene Größen auf unterschiedlichen Plattformen sein, so ist ein Rohrkrepierer für ein plattformunabhängiges Format. Sie müssen entscheiden, welche Wertebereich in der Datei gespeichert werden muss. 32 Bit ist wahrscheinlich am einfachsten.

Sie sagen, dass Sie nicht besorgt über andere Plattformen noch . Ich werde das so verstehen Sie die Möglichkeit, sie unterstützen behalten wollen, in diesem Fall sollten Sie die Byte-Reihenfolge der Dateiformat definieren. x86 ist Little-Endian, so dass Sie vielleicht denken, dass der beste ist. Aber Big-Endian ist die „Standard“ Austausch Ordnung, wenn etwas ist, da es in der Vernetzung verwendet wird.

Wenn Sie sich für Big-Endian ( "Netzwerk-Byte-Reihenfolge"):

// can't be bothered to support really crazy platforms: it is in
// any case difficult even to exchange files with 9-bit machines,
// so we'll cross that bridge if we come to it.
assert(CHAR_BIT == 8);
assert(sizeof(uint32_t) == 4);

{
    // write value
    uint32_t value = 23;
    const uint32_t networkOrderValue = htonl(value);
    fwrite(&networkOrderValue, sizeof(uint32_t), 1, file);
}

{
    // read value
    uint32_t networkOrderValue;
    fread(&networkOrderValue, sizeof(uint32_t), 1, file);
    uint32_t value = ntohl(networkOrderValue);
}

Eigentlich brauchen Sie nicht einmal zwei Variablen zu erklären, es ist nur ein bisschen verwirrend „Wert“ mit seinem Netzwerk, um entsprechenden Betrag in den gleichen Variablen zu ersetzen.

Es funktioniert, weil „Netzwerk-Byte-Reihenfolge“ definiert ist, zu sein, was Anordnung der Bits führen zu einem austauschbaren (Big-Endian), um im Speicher. Keine Notwendigkeit, Durcheinander mit den Gewerkschaften, weil jedes gespeicherte Objekt in C kann als eine Folge von Zeichen behandelt werden. Keine Notwendigkeit, einen speziellen Fall für endianness, weil das ist, was ntohl / htonl sind.

Wenn diese zu langsam ist, können Sie über teuflisch optimierte plattformspezifische Byte-Swapping zu denken beginnen, mit SIMD oder was auch immer. Oder mit Little-Endian, unter der Annahme, dass die meisten Ihrer Plattformen werden Little-Endian und so ist es schneller „im Durchschnitt“ über sie. In diesem Fall werden Sie brauchen „Host Little-Endian“ und „Little-Endian-Host“ Funktionen zu schreiben oder zu finden, die natürlich auf x86 nur nichts zu tun.

ich glaube, die meisten Cross-Architektur-Ansatz ist es, die uintXX_t Typen zu verwenden, wie in stdint.h definiert. Manpage hier. zum Beispiel ein int32_t Sie auf x86 eine 32-Bit-Integer geben und x86-64. Ich benutze diese nun standardmäßig in allen meinen Code und haben keine Probleme hatte, wie sie in allen * NIX ziemlich Standard sind.

sizeof(uint32_t) == 4 Angenommen, es ist möglich Bytereihenfolgen 4!=24, davon Little-Endian und Big-Endian sind die prominentesten Beispiele, aber andere haben auch verwendet worden (z PDP-Endian).

Hier sind Funktionen zum Lesen und Schreiben von 32-Bit Integer ohne Vorzeichen aus einem Strom, eine beliebige Bytereihenfolge beachtend, die durch die ganze Zahl, deren Darstellung angegeben wird, ist die Bytefolge 0,1,2,3: endian.h , endian.c

Der Header definiert diese Prototypen

_Bool read_uint32(uint32_t * value, FILE * file, uint32_t order);
_Bool write_uint32(uint32_t value, FILE * file, uint32_t order);

und diese Konstanten

LITTLE_ENDIAN
BIG_ENDIAN
PDP_ENDIAN
HOST_ORDER
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top