2009-07-09 20 views
2

私は、いくつかの異なるオペレーティングシステムとコンピュータで書き込まれ、読み込まれるべきファイル形式に取り組んでいます。これらのコンピュータの中には、x86マシンでなければならないものもあれば、x86-64でもあるものもあります。いくつかの他のプロセッサが存在するかもしれませんが、私はそれらについて心配していませんまだCコードでのlong int値の書き込みと読み込み

このファイル形式は次のように読まれることになるいくつかの数字を含める必要があります。これは私のコンピュータ上で動作しているようだが、私は心配

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); 
} 

:として

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; 
} 

と書かれたがそれは他人にはないかもしれないし、ファイル形式がコンピュータ間で異なってしまうかもしれないということです(例えば32ビット対64ビットコンピュータ)。 何か間違っていますか?数字ごとに一定のバイト数を使用するようにコードを実装するにはどうすればよいですか?

freadを使用するだけでいいですか(代わりに私のコードも速くなります)?

答えて

8

stdint.hのタイプを使用して、同じバイト数の入出力を確保してください。

次にエンディアンの問題に取り組んでいるだけですが、実際には というコードは扱わないでしょう。

エイリアス化されたchar *を使用してlongをシリアライズすると、異なるエンディアンを持つプラットフォーム用に、書かれたファイルに異なるバイトオーダーが残ります。

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

をとのようなものを使用して、その後再構成:

あなたはそうのようなバイトの何かを分解する必要があります。また、endiannessで問題に遭遇するかもしれません

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

@GMan - ないあなたの時間(同じプラットフォームのエンディアンに基づいて条件付きで別の定義をコンパイルしない限り)ユニオンで同じ問題が発生しますか? –

+0

stdintへの参照は非常に役に立ち、多くの助けになります! – luiscubal

+0

符号なしの文字または符号拡張を使用するとあなたが噛み付きます。 –

1

。何が起こる可能性のある移植性の問題を処理するNetCDFまたはHDFのようなものを使用しないのはなぜですか?

1

のではなく、それらの文字を持つ構造体を使用して、より多くの数学的なアプローチを検討してください。

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

これは、あなたが達成しようとしているのかについて、もう少し直接的かつ明確です。また、大きな数値を処理するためにループで実装することもできます。

+0

これはビッグエンディアン形式でファイルを読み込みます。これはおそらく良いことですが、 'long '全体を読み込み、それをメモリ上で' bswap'するのはもっと速いでしょう。 – ephemient

+0

@ephemient:bswapする必要があると仮定します(ビッグエンディアンの場合はどうなりますか?)。また、bswapが動作すると仮定すると(もしあなたのロングが64ビットであれば、それともあなたはいくつかのミドルエンディアンマシンを放棄していますか?) –

+0

まあ、 "必要ならばbswap"と思っていましたが、ミドルエンディアンマシンについて考えることはできません(過去20年以内に存在していましたか?) 's/bswap/ntohl /'はどうでしょうか?私が知る限り、一般的な実装では64ビットの値が与えられれば上位32ビットを落としますが、それは正しいことです。 – ephemient

1

long intを使用したくないです。これはプラットフォームによって異なることがあります。したがって、プラットフォームに依存しないフォーマットのための非スターターです。ファイルに格納する必要がある値の範囲を決定する必要があります。 32ビットがおそらく最も簡単です。

あなたは他のプラットフォームについては心配していません。まだです。私はあなたがそれらをサポートする可能性を保持したいという意味にします。この場合、ファイル形式のバイトオーダーを定義する必要があります。 x86はリトルエンディアンなので、それが最高だと思うかもしれません。しかし、ビッグエンディアンはネットワーキングで使用されているため、何かがあれば "標準"交換命令です。

あなたはビッグエンディアン(「ネットワークバイト順序」)のために行く場合:

// 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); 
} 

実際に、あなたも、2つの変数を宣言する必要はありません、それはとの「価値」を交換するだけで少し混乱ですそのネットワーク変数は同じ変数に相当します。

「ネットワークバイトオーダー」は、どのようなビット配置であってもメモリ内で互換性のある(ビッグエンディアン)オーダ​​ーとなるように定義されているために機能します。 C言語のストアドオブジェクトはすべてcharのシーケンスとして扱うことができるため、共用体を混乱させる必要はありません。それはntohl/htonlのためのエンディアンの特別なケースは必要ありません。

これが遅すぎる場合は、SIMDなどを使用して、プラットフォーム固有のプラットフォーム固有のバイトスワッピングを考えてみることができます。または、ほとんどのプラットフォームがリトルエンディアンであることを前提として、リトルエンディアンを使用しているため、それらの間で「平均して」より速くなります。その場合、「ホストからリトルエンディアンへ」および「リトルエンディアンからホストへ」の関数を書くか見つけなければなりません。もちろん、x86では何もしません。

0

私は、stdint.hで定義されているように、uintXX_t型を使用するのが最も一般的なアーキテクチャアプローチだと考えています。 See man page here.例えば、int32_tはx86とx86-64上で32ビット整数を与えます。 すべてのコードでこれらをデフォルトで使用していますが、すべての* NIXで標準的なので問題はありませんでした。

0

とすると、リトルエンディアンとビッグエンディアンが最も顕著な例であるが、他のものも同様に使用されている(例えば、PDPエンディアン)バイトオーダーは4!=24となる。ヘッダは、これらのプロトタイプを定義endian.hendian.c

:ここ

は、その表現バイトシーケンス0,1,2,3整数で指定された任意のバイト順序を聞き入れ、ストリームから32ビット符号なし整数を読み書きするための関数であります

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

およびこれらの定数

LITTLE_ENDIAN 
BIG_ENDIAN 
PDP_ENDIAN 
HOST_ORDER 
関連する問題