web-dev-qa-db-ja.com

バイナリファイルから読み取るときにビッグエンディアンをリトルエンディアンに変換する

ビッグエンディアンをリトルエンディアンに変換する方法を検討してきました。しかし、私は自分の問題を解決できるものを見つけられませんでした。この変換を行う方法はたくさんあるようです。とにかく、次のコードはビッグエンディアンシステムで問題なく動作します。しかし、リトルエンディアンシステムでも機能するように変換関数をどのように記述すればよいでしょうか。

これは宿題ですが、学校のシステムがビッグエンディアンシステムを実行しているため、これは単なる追加です。それはちょうど私が興味を持ち、それを私の自宅のコンピューターでも動作させたいと思っただけです

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
   ifstream file;

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

   if(!file)
      cerr << "Not able to read" << endl;
   else
   {
      cout << "Opened" << endl;

      int i_var;
      double d_var;

      while(!file.eof())
      {
         file.read( reinterpret_cast<char*>(&i_var) , sizeof(int) );
         file.read( reinterpret_cast<char*>(&d_var) , sizeof(double) );
         cout << i_var << " " << d_var << endl;
      }
   }
   return 0;
}

解決済み

したがって、ビッグエンディアンVSリトルエンディアンは、バイトの逆順にすぎません。私が書いたこの機能はとにかく私の目的を果たすようです。他の誰かが将来必要とする場合に備えて、ここに追加しました。ただし、これは倍精度浮動小数点数専用です。整数の場合はtorak関数を使用するか、4バイトだけをスワップしてこのコードを変更できます。

double swap(double d)
{
   double a;
   unsigned char *dst = (unsigned char *)&a;
   unsigned char *src = (unsigned char *)&d;

   dst[0] = src[7];
   dst[1] = src[6];
   dst[2] = src[5];
   dst[3] = src[4];
   dst[4] = src[3];
   dst[5] = src[2];
   dst[6] = src[1];
   dst[7] = src[0];

   return a;
}
18
starcorn

あなたが続けるつもりであると仮定すると、ヘルパー関数の小さなライブラリー・ファイルを保持しておくと便利です。これらの関数のうち2つは、4バイト値と2バイト値のエンディアンスワップである必要があります。いくつかの確かな例(コードを含む)については、 この記事 を確認してください。

スワップ関数を取得したら、間違ったエンディアンの値を読み取るたびに、適切なスワップ関数を呼び出します。ここの人々にとって時々つまずきのポイントは、シングルバイト値をエンディアンスワップする必要がないということです。そのため、ファイルからの文字列を表す文字ストリームのようなものを読んでいる場合、それは良いことです。値を読み込む場合にのみ、これは複数バイト(整数値など)であり、それらを交換する必要があります。

2
Bryan

データ型に対して一般化されるエンディアンスワップ用のテンプレートを使用できます。

#include <algorithm>

template <class T>
void endswap(T *objp)
{
  unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
  std::reverse(memp, memp + sizeof(T));
}

その後、コードは次のようになります。

file.read( reinterpret_cast<char*>(&i_var) , sizeof(int) );
endswap( &i_var );
file.read( reinterpret_cast<char*>(&d_var) , sizeof(double) );  
endswap( &d_var );
cout << i_var << " " << d_var << endl;  
21
Dingo

Linuxはendian.hを提供します。これには、最大64ビットの効率的なエンディアンスワップルーチンがあります。また、システムのエンディアンを自動的に考慮します。 32ビット関数は次のように定義されています。

uint32_t htobe32(uint32_t Host_32bits);           // Host to big-endian encoding
uint32_t htole32(uint32_t Host_32bits);           // Host to lil-endian encoding
uint32_t be32toh(uint32_t big_endian_32bits);     // big-endian to Host encoding
uint32_t le32toh(uint32_t little_endian_32bits);  // lil-endian to Host encoding

16ビットと64ビットの同様の名前の関数を使用します。だからあなたはただ言う

 x = le32toh(x);

リトルエンディアンエンコーディングの32ビット整数をホストCPUエンコーディングに変換します。これは、リトルエンディアンデータを読み取る場合に役立ちます。

 x = htole32(x);

hostエンコーディングから32ビットリトルエンディアンに変換されます。これは、リトルエンディアンデータを書き込む場合に役立ちます。

BSDシステムでは、同等のヘッダーファイルはsys/endian.hです。

4
QuasarDonkey

ntohl 関数ファミリーに興味があるかもしれません。これらは、ネットワークからホストのバイト順にデータを変換するように設計されています。ネットワークのバイト順はビッグエンディアンなので、ビッグエンディアンシステムでは何も実行されませんが、リトルエンディアンシステムでコンパイルされた同じコードは適切なバイトスワップを実行します。

4
torak

MSがこれをVSでサポートしていることも付け加えるとよいでしょう。このインライン関数を確認してください。

  • htond
  • htonf
  • htonl
  • トン
  • htons
2
Mahmoud Fayez