ビッグエンディアン構造体をリトルエンディアン構造体に変換するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/859535

  •  21-08-2019
  •  | 
  •  

質問

UNIX マシンで作成されたバイナリ ファイルがあります。それはただ次々に書かれた記録の束です。レコードは次のように定義されます。

struct RECORD {
  UINT32 foo;
  UINT32 bar;
  CHAR fooword[11];
  CHAR barword[11];
  UNIT16 baz;
}

Windows マシンでこのデータをどのように読み取って解釈するかを考えています。私は次のようなものを持っています:

fstream f;
f.open("file.bin", ios::in | ios::binary);

RECORD r;

f.read((char*)&detail, sizeof(RECORD));

cout << "fooword = " << r.fooword << endl;

大量のデータを取得しましたが、それは期待したデータではありませんでした。私の問題はマシンのエンディアンの違いに関係しているのではないかと思われるので、それについて質問することにしました。

複数のバイトが Windows ではリトルエンディアンで格納され、UNIX 環境ではビッグエンディアンで格納されることを理解しています。2 バイトの場合、Windows の 0x1234 は、UNIX システムでは 0x3412 になります。

エンディアンは、構造体全体のバイト順序、または構造体の個々のメンバーのバイト順序に影響しますか?UNIX システムで作成された構造体を、Windows システム上で同じデータを持つ構造体に変換するには、どのようなアプローチをとればよいでしょうか?数バイトのバイト順よりも深いリンクも素晴らしいでしょう。

役に立ちましたか?

解決

と同様にエンディアン、あなたは、2つのプラットフォーム間のパディングの違いを認識する必要があります。もし奇数長チャーアレイと16ビット値を有する場合は特に、あなたもいくつかの要素の間のパッドバイトの異なる数を見出すことができる。

編集:構造をなし梱包で書き出された場合、それはかなり簡単にする必要があります。この(未テスト)コードのようなものは、仕事をする必要があります:

// Functions to swap the endian of 16 and 32 bit values

inline void SwapEndian(UINT16 &val)
{
    val = (val<<8) | (val>>8);
}

inline void SwapEndian(UINT32 &val)
{
    val = (val<<24) | ((val<<8) & 0x00ff0000) |
          ((val>>8) & 0x0000ff00) | (val>>24);
}
あなたは構造体をロードしたら、

すると、ちょうど各要素をスワップます:

SwapEndian(r.foo);
SwapEndian(r.bar);
SwapEndian(r.baz);

他のヒント

実際には、エンディアンが基盤となるハードウェアではなく、OSの財産です。

最善の解決策は、データ書き込み時に標準に変換することである - 。「ネットワークバイト順序」のためのGoogleに、あなたはこれを行うための方法を見つける必要があります。

編集:ます。http:// WWWここにリンクがあります。 gnu.org/software/hello/manual/libc/Byte-Order.htmlする

ファイルから構造体に直接読んではいけません!梱包は異なる場合があります、あなたは、プラグマパックまたは類似のコンパイラの特定の構造をいじる必要があります。あまりにも信頼できません。そのコードはアーキテクチャやシステムの広い数でコンパイルされていないので、プログラマーの多くはこれで逃げるが、それはそれを行うためのOKことだという意味ではありません!

良い別のアプローチは、バッファに、どのような、ヘッダを読み取り、符号なし32ビット整数を読み取るようにアトミック操作でI / Oのオーバーヘッドを回避するために、3つから解析することである!

char buffer[32];
char* temp = buffer;  

f.read(buffer, 32);  

RECORD rec;
rec.foo = parse_uint32(temp); temp += 4;
rec.bar = parse_uint32(temp); temp += 4;
memcpy(&rec.fooword, temp, 11); temp += 11;
memcpy(%red.barword, temp, 11); temp += 11;
rec.baz = parse_uint16(temp); temp += 2;

parse_uint32の宣言は次のようになります:

uint32 parse_uint32(char* buffer)
{
  uint32 x;
  // ...
  return x;
}

これは非常に単純な抽象化され、同様にポインタを更新するために、実際には余分な費用がかかりません。

uint32 parse_uint32(char*& buffer)
{
  uint32 x;
  // ...
  buffer += 4;
  return x;
}

後フォームは、バッファを解析するためのクリーンなコードを可能にします。あなたが入力から解析する際に、ポインタが自動的に更新されます。

同様に、memcpyのはヘルパーを持っている可能性があり、何かます:

void parse_copy(void* dest, char*& buffer, size_t size)
{
  memcpy(dest, buffer, size);
  buffer += size;
}

このような配置の美しさは、あなたが名前空間を持つことができるということです「LITTLE_ENDIAN」と「BIG_ENDIAN」、そして、あなたのコードでこれを行うことができます:

using little_endian;
// do your parsing for little_endian input stream here..

簡単に、同じコードのためにかかわらず、ほとんど必要ない機能をエンディアンを切り替える。..ファイル形式は通常、とにかく固定エンディアンを持っています。

仮想メソッドを持つクラスにこれを抽象化しないでください。ただオーバーヘッドを追加しますが、お気軽かのように傾斜する:

little_endian_reader reader(data, size);
uint32 x = reader.read_uint32();
uint32 y = reader.read_uint32();

リーダーオブジェクトは明らかに単にポインタの周りに薄いラッパーであろう。もしあればサイズパラメータは、エラーチェックのためになります。それ自体はインタフェースのための本当に必須ではありません。

(私たちはlittle_endian_readerオブジェクトを作成するため)エンディアンの一品は、ここでコンパイル時に行われたかに注目してください、私たちはありません、特に正当な理由のための仮想メソッドのオーバーヘッドを呼び出すので、私はこのアプローチに行かないだろう。 ; - )

、あなたの好みに合わせてデータを整理し、必ずしもすべての任意の特定の構造体にそれを読むことができているようアラウンド「なFileFormat構造体」を維持する本当の理由はありません。この段階では、結局、それはただのデータです。あなたは、画像のようなファイルを読んだとき、あなたは本当に周りのヘッダーを必要としません..あなたは、すべてのファイルタイプで同じであるあなたのイメージコンテナを持っていなければならないので、特定の形式を読み取るためのコードだけで、ファイルを読み込み、解釈し、再フォーマットする必要がありペイロードをデータ&保存します。 =)

私が意味する、これは複雑に見えますか?

uint32 xsize = buffer.read<uint32>();
uint32 ysize = buffer.read<uint32>();
float aspect = buffer.read<float>();    

のコードは、その見栄え、本当に低オーバーヘッドすることができます!エンディアンがファイルとアーキテクチャのためのコードがために同じコンパイルされている場合は、innerloopは次のように見ることができます:

uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
その最適化は悪い考え、より遅い使用する場合がありますので、

これは、いくつかのアーキテクチャ上の違法かもしれませんが、より堅牢なアプローチます:

uint32 value = ptr[0] | (static_cast<uint32>(ptr[1]) << 8) | ...; ptr += 4;
return value;

メソッドがインラインされた場合、合理的に低いオーバーヘッドであるBSWAPまたはMOVにコンパイルすることができるのx86、オン;コンパイラはかなり効率的である中間コード、何もない、に「移動」ノードを挿入します。アライメントに問題がある場合は、完全な読み取りシフトやシーケンス生成される可能性があります、outch、まだありませんあまりにもみすぼらしいです。比較分岐は、アドレスのLSBをテストする場合、最適化を可能にし、構文解析の高速または低速バージョンを使用することができるかどうかを確認できます。しかし、これは、すべての読み取りでのテストのためのペナルティを意味します。努力する価値はないかもしれません。

ああ、そう、私たちはヘッダとものを読んでいる、私はそれがあまりにも多くのアプリケーションのボトルネックであるとは思いません。一部のコーデックは、いくつかの本当にTIGHT innerloopを行っている場合は、再度、一時バッファに読み込み、そこからデコードをすることはよくadvicedです。同じ原理大量のデータを処理する際に...誰もがファイルからバイト・アット・時間を読みません。まあ、実際に、私は非常に多くの場合、コードのようなものを見て、「あなたは、なぜそれが」通常の応答は、ファイルシステムがブロックを読み込み、バイトが真、とにかくメモリから来るが、彼らは深いコールスタックを通過することをやるということであるためにで数を取得するための高オーバーヘッドはありますTES!

それでも、一度パーサのコードを記述し、無数の時間を使う - 。>叙事詩勝利

ファイルから構造体に直接読む:!ITのFOLKSをしないでください。

これは、独立して、各メンバー全体ではなく、structに影響を与えます。また、配列のようなものには影響しません。例えば、それはちょうど逆の順序で格納されているintsのバイトになります。

PS。それは奇妙なエンディアンとマシンがあるかもしれません、と述べました。私はちょうど最も使用されるマシン(x86の、ARM、PowerPCの、SPARC)に適用されると述べています。

あなたは個別に、複数バイトの各メンバーのエンディアンを修正する必要があります。彼らはバイトのシーケンスとして見ることができるように文字列は、(foowordとbarword)に変換する必要はありません。

しかし、あなたは別の問題の世話をする必要があります:あなたの構造体のメンバーのaligmenentを。 sizeof(RECORD)は、UNIXとWindowsのコードの両方で同じであれば基本的に、あなたは確認する必要があります。コンパイラは通常、あなたが望むaligment(例えば、の#pragma pack)を定義するためにプラグマを提供します。

2 つのコンパイラ間のアライメントの違いも考慮する必要があります。各コンパイラーは、アーキテクチャーに最適な構造体のメンバー間にパディングを挿入できます。したがって、次のことを本当に知っておく必要があります。

  • UNIX プログラムがファイルに書き込む方法
  • オブジェクトのバイナリ コピーの場合は、構造体の正確なレイアウト。
  • バイナリ コピーの場合、ソース アーキテクチャのエンディアンは何ですか。

これが、ほとんどのプログラム (私が見たプログラム (プラットフォームに中立である必要がある)) が、標準の iostream で簡単に読み取れるテキスト ストリームとしてデータをシリアル化する理由です。

私はこのように、スワップが必要な各データ型のSwapBytesメソッドを実装したいです

inline u_int ByteSwap(u_int in)
{
    u_int out;
    char *indata = (char *)&in;
    char *outdata = (char *)&out;
    outdata[0] = indata[3] ;
    outdata[3] = indata[0] ;

    outdata[1] = indata[2] ;
    outdata[2] = indata[1] ;
    return out;
}

inline u_short ByteSwap(u_short in)
{
    u_short out;
    char *indata = (char *)&in;
    char *outdata = (char *)&out;
    outdata[0] = indata[1] ;
    outdata[1] = indata[0] ;
    return out;
}

それから私はこのように、スワップ必要な構造に機能を追加します:

struct RECORD {
  UINT32 foo;
  UINT32 bar;
  CHAR fooword[11];
  CHAR barword[11];
  UNIT16 baz;
  void SwapBytes()
  {
    foo = ByteSwap(foo);
    bar = ByteSwap(bar);
    baz = ByteSwap(baz);
  }
}

次に、あなたは、読み込み(または書き込み)あなたのコードを変更することができ、このような構造ます:

fstream f;
f.open("file.bin", ios::in | ios::binary);

RECORD r;

f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();

cout << "fooword = " << r.fooword << endl;

あなただけの各BYTESWAP過負荷のプラットフォーム固有の実装を持っている必要があります。

異なるプラットフォームをサポートするために、

このような何か作業をする必要があります:

#include <algorithm>

struct RECORD {
    UINT32 foo;
    UINT32 bar;
    CHAR fooword[11];
    CHAR barword[11];
    UINT16 baz;
}

void ReverseBytes( void *start, int size )
{
    char *beg = start;
    char *end = beg + size;

    std::reverse( beg, end );
}

int main() {
    fstream f;
    f.open( "file.bin", ios::in | ios::binary );

    // for each entry {
    RECORD r;
    f.read( (char *)&r, sizeof( RECORD ) );
    ReverseBytes( r.foo, sizeof( UINT32 ) );
    ReverseBytes( r.bar, sizeof( UINT32 ) );
    ReverseBytes( r.baz, sizeof( UINT16 )
    // }

    return 0;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top