3

システムを Python から C++ に変換する作業を行っています。通常は Python を使用して実行されるstruct.unpack(バイナリ文字列を数値として解釈する) アクションを C++ で実行できるようにする必要があります。整数値の場合、次のデータ型を使用して、これを (一種の) 機能させることができますstdint.h

struct.unpack("i", str) ==> *(int32_t*) str; //str is a char* containing the data

これは、リトル エンディアンのバイナリ文字列では適切に機能しますが、ビッグ エンディアンのバイナリ文字列では失敗します。>基本的に、 struct.unpack でタグを使用するのと同等のものが必要です。

struct.unpack(">i", str) ==> ???

これを行うためのより良い方法があれば、私はすべての耳です。ただし、c++11 も、Boost 以外のサードパーティ製ライブラリも使用できません。struct.unpack(">f", str)と のように floatとdouble を解釈できるようにする必要もありstruct.unpack(">d", str)ますが、これを解決するときにそれを取得します。

: この場合、マシンのエンディアンは無関係であることを指摘しておく必要があります。コードで受信するビットストリームは常にビッグ エンディアンであることを知っています。そのため、常にビッグ エンディアンのケースをカバーするソリューションが必要です。コメントでBoBTFishが指摘した記事は、解決策を提供しているようです.

4

5 に答える 5

7

32ビットおよび16ビット値の場合:

これはまさに、ビッグエンディアンであるネットワークデータの問題です。ntohlを使用して、32ビットをホストオーダー(この場合はリトルエンディアン)に変換できます。

ntohl()関数は、符号なし整数netlongをネットワークバイトオーダーからホストバイトオーダーに変換します。

int res = ntohl(*((int32_t) str)));

これにより、ホストがビッグエンディアンで何も実行されない場合も処理されます。

64ビット値の場合

非標準のLinux/BSDでは、C ++の64ビットntohl()を見ることができますか?htobe64を指します

これらの関数は、整数値のバイトエンコーディングを、現在のCPU(「ホスト」)が使用するバイトオーダーから、リトルエンディアンおよびビッグエンディアンのバイトオーダーに変換します。

Windowsの場合:C ++でビッグエンディアンとリトルエンディアンの値を変換するにはどうすればよいですか?

これは、_byteswap_uint64と、16ビットおよび32ビットのソリューションとgcc固有の__builtin_bswap(32/64)呼び出しを指します。

他のサイズ

ほとんどのシステムには、16/32/64ビット長でない値はありません。その時点で、64ビット値で格納し、シフトして変換しようとする場合があります。私はいくつかの良いテストを書きます。珍しい状況だと思います。詳細が役立つと思います。

于 2012-12-13T16:31:04.833 に答える
4

一度に 1 バイトずつ文字列をアンパックします。

unsigned char *str;
unsigned int result;

result =  *str++ << 24;
result |= *str++ << 16;
result |= *str++ << 8;
result |= *str++;
于 2012-12-13T16:20:03.043 に答える
2

まず、あなたがしているキャスト:

char *str = ...;
int32_t i = *(int32_t*)str;

厳密なエイリアシング ルールにより、未定義の動作が発生します ( のstrようなもので初期化されていない限りint32_t x; char *str = (char*)&x;)。実際には、このキャストによってアライメントされていない読み取りが発生し、一部のプラットフォームではバス エラー (クラッシュ) が発生し、他のプラットフォームではパフォーマンスが低下する可能性があります。

代わりに、次のようなことを行う必要があります。

int32_t i;
std::memcpy(&i, c, sizeof(i));

ホストのネイティブなバイト順序とホストに依存しない順序との間でバイトを交換するための関数がいくつntoh*()hton*()あります: ホストが異なればバイト順序も異なる可能性があるため、読み取るデータがすべてのプラットフォームで一貫したシリアル化された形式を使用する場合は、これを使用することをお勧めします。*ls

ntoh(i);

strバイトを整数にコピーする前に、手動でバイトを移動することもできます。

std::swap(str[0],str[3]);
std::swap(str[1],str[2]);
std::memcpy(&i,str,sizeof(i));

または、シフトとビット単位の演算子を使用して、整数の値を手動で操作できます。

std::memcpy(&i,str,sizeof(i));
i = (i&0xFFFF0000)>>16 | (i&0x0000FFFF)<<16;
i = (i&0xFF00FF00)>>8  | (i&0x00FF00FF)<<8;
于 2012-12-13T16:24:38.973 に答える
0

これは、ビットをいじる領域に分類されます。

for (i=0;i<sizeof(struct foo);i++) dst[i] = src[i ^ mask]; 

ここで、保存されたエンディアンとネイティブエンディアンが異なる場合は、mask ==(sizeof type -1)です。

この手法を使用すると、構造体をビットマスクに変換できます。

 struct foo {
    byte a,b;       //  mask = 0,0
    short e;        //  mask = 1,1
    int g;          //  mask = 3,3,3,3,
    double i;       //  mask = 7,7,7,7,7,7,7,7
 } s; // notice that all units must be aligned according their native size

この場合も、これらのマスクはシンボルごとに2ビットでエンコードできます。(1<<n)-1つまり、64ビットマシンでは、32バイトサイズの構造体の必要なマスクを単一の定数(1、2、4、および8バイトの配置)でエンコードできます。

unsigned int mask = 0xffffaa50;  // or zero if the endianness matches
for (i=0;i<16;i++) { 
     dst[i]=src[i ^ ((1<<(mask & 3))-1]; mask>>=2;
}
于 2012-12-13T16:21:50.030 に答える
-1

受け取った値が真の文字列 (char* または std::string) であり、それらの形式情報、sscanf()、atoi() を知っている場合、まあ、本当に ato() はあなたの友達になります。それらは適切にフォーマットされた文字列を受け取り、渡されたフォーマット (一種の逆 printf) に従って変換します。

于 2012-12-13T16:28:10.260 に答える