64 ビット システムでの 32 ビット パックされたバイナリ データの読み取り
質問
パックされたバイナリ データ (構造体の構造体として保存されます) を読み取り、それを Python オブジェクトに解析する Python C 拡張機能を作成しようとしています。32 ビット マシンではすべてが期待どおりに動作します (バイナリ ファイルは常に 32 ビット アーキテクチャで書き込まれます) が、64 ビット マシンではそうではありません。これを行うための「推奨」方法はありますか?
投稿するコードは膨大になりますが、例としては次のとおりです。
struct
{
WORD version;
BOOL upgrade;
time_t time1;
time_t time2;
} apparms;
File *fp;
fp = fopen(filePath, "r+b");
fread(&apparms, sizeof(apparms), 1, fp);
return Py_BuildValue("{s:i,s:l,s:l}",
"sysVersion",apparms.version,
"powerFailTime", apparms.time1,
"normKitExpDate", apparms.time2
);
32 ビット システムではこれはうまく機能しますが、64 ビットでは time_t サイズが異なります (32 ビット長と 64 ビット長)。
くそー、あなたたちは速いです。
パトリック、私は最初に struct パッケージを使い始めましたが、私のニーズにとっては遅すぎることがわかりました。さらに、Python 拡張機能を作成する口実を探していました。
愚かな質問であることは承知していますが、どのタイプに注意する必要がありますか?
ありがとう。
解決
データ型を明示的に指定します (例:整数) は 32 ビットです。それ以外の場合、2 つの整数が隣り合って読み取られる場合、それらは 1 つの 64 ビット整数として読み取られます。
クロスプラットフォームの問題に対処する場合、注意すべき主な点は次の 2 つです。
- 苦味。パックされたデータが 32 ビット整数で書き込まれている場合、すべてのコードは読み取り時に明示的に 32 ビット整数を指定する必要があります。 そして 書き込み。
- バイトオーダー。コードを Intel チップから PPC または SPARC に移動すると、バイト順序が間違ってしまいます。データをインポートしてバイトフリップして、現在のアーキテクチャと一致させる必要があります。それ以外の場合は 12 (
0x0000000C
) は 201326592 (0x0C000000
).
これがお役に立てば幸いです。
他のヒント
「struct」モジュールはこれを実行できるはずですが、データの途中での構造体の位置合わせが常に問題になります。ただし、正しく理解するのはそれほど難しいことではありません。構造体内の構造体がどの境界に位置合わせされているかを (一度) 見つけて、その境界まで ('x' 指定子を使用して手動で) パディングします。struct.calcsize() を実際のデータと比較することで、パディングを再確認できます。確かに、C 拡張機能を作成するよりも簡単です。
このように Py_BuildValue() を使い続けるには、2 つのオプションがあります。コンパイル時に time_t のサイズ (基本的な型の観点から、つまり「int」、「long」、または「ssize_t」) を決定し、Py_BuildValue に適切なフォーマット文字 (int の場合は「i」) を使用できます。 「l」はlong、「n」はssize_tを表します。または、PyInt_FromSsize_t() を手動で使用することもできます。その場合、コンパイラーがアップキャストを実行し、「O」形式の文字を使用して結果を Py_BuildValue に渡します。
構造体にアーキテクチャに依存しないメンバーを使用していることを確認する必要があります。たとえば、int は、あるアーキテクチャでは 32 ビットであり、別のアーキテクチャでは 64 ビットである可能性があります。他の人が提案したように、 int32_t
代わりにスタイルタイプを使用します。構造体に位置合わせされていないメンバーが含まれている場合は、コンパイラによって追加されたパディングにも対処する必要がある場合があります。
クロス アーキテクチャ データに関するもう 1 つの一般的な問題は、エンディアンです。Intel i386 アーキテクチャはリトルエンディアンですが、完全に異なるマシン (例:Alpha または Sparc)、これについても心配する必要があります。
Python struct モジュールは、フォーマット文字列の一部として渡されたプレフィックスを使用して、これら両方の状況に対処します。
- @ - ネイティブのサイズ、エンディアン、およびアライメントを使用します。i= sizeof(int)、l= sizeof(long)
- = - ネイティブ エンディアンを使用しますが、標準のサイズとアライメント (i=32 ビット、l=64 ビット)
- < - リトルエンディアンの標準サイズ/アライメント
- ビッグエンディアンの標準サイズ/アライメント
一般に、データがマシンから送信される場合は、エンディアンとサイズ/パディング形式を特定のものに固定する必要があります。形式として「<」または「>」を使用してください。C 拡張機能でこれを処理したい場合は、それを処理するためのコードを追加する必要がある場合があります。
バイナリデータを読み取るためのコードは何ですか?データを次のような適切なサイズの型にコピーしていることを確認してください。 int32_t
ただの代わりに int
.
なぜ使用しないのですか 構造体 パッケージ?