3

私には困難な状況があります。Mac、PC、iOS、Androidのプログラムで、レガシー形式のファイルを受信し、それらのファイルからデータを解析します。これらのファイルの作成方法を変更することはできません。

ファイルは、C ++プログラムが構造体に数値と文字列を入力し、それを書き出すことによって生成されます。これがサニタイズされたバージョンです。

struct MyObject {
String Kfkj(MAXKYS); 
String Oern(MAXKYS);
String Vdflj(MAXKYS, 9); 
int Muic;
int Tdfkj;
int VdfkAsdk;
int SsdjsdDsldsk; 
int Ndsoief; 
String TdflsajPdlj; 
String TdckjdfPas; 
String AdsfakjIdd;
int IdkfjdKasdkj;
int AsadkjaKadkja(MAXKYS); 
int Kasldsdkj;
bool Usadl;
String PsadkjOasdj(9); 
String PasdkjOsdkj;
};

ご覧のとおり、プリミティブと文字列。

次に、ファイルに書き出す方法を次に示します。

MyInstance MyObject;
FileName = "C:\MyFile.ab2"
ofstream fout (FileName, ios::binary);
fout.write((char*)& MyInstance, sizeof(MyInstance));

一度翻訳してから他のプラットフォームにファイルを配布するオプションはありません。私たちはそれをすべての異なるプラットフォームで翻訳する必要があり、これが私たちが取り組む必要のあることです。C ++がデータをシリアル化する方法についての情報をいただければ幸いです。そのため、ファイルを解析する方法を知っています。

編集:ソリューション

ここで複数の回答から受け取ったフィードバックは非常に役に立ちました。それを使用して、16進エディターで広範な分析を行い、次のことを発見しました。

  • 要素は次々にファイルに入ります
  • この場合の「文字列」は、その文字列のintに続く文字数を表すintで始まります。文字列が存在しない場合でも、値が0のintが含まれます。
  • 私が見たファイルとマシンの整数は、2バイトで、リトルエンディアンであり、ほとんど署名されていません(署名されたものがいくつかありましたが、私をつま先に置いておくためです)
  • ブール値は2バイトで、明らかに-1(FF FF)は「真」を表します

これまでのところ、さまざまなデバイスでさまざまなパディングやエンディアンの問題が発生したことはありませんが、これらは非常に現実的な懸念事項です。これらの回答の熟練したメモと警告は、プラットフォーム間でデータをオンラインで転送するために、XMLやJSONなどの脆弱性の低い代替手段に変更するようにクライアントを説得するための弾薬を提供します。

開発者が解雇されたかどうかを尋ねる人は...まあ、彼らのコードは非常に古いとだけ言っておきましょう。しかし、何度も話し合った後でも、C++構造体を書き出して別のプラットフォームで読み込もうとするのに苦労しています。良い考えではありません。

4

7 に答える 7

4

おっと-それはクレイジーだ。では、Stringオブジェクトにはポインタが含まれていませんか?してはいけません-これは機能しているコードであると主張しているからです。

とにかく、そのコードはシリアル化を行っていません。構造をメモリに配置されたとおりにファイルに書き出すだけです。唯一の問題は、一部のプラットフォームでは、intなどの整数型のパディングとsizeが異なる場合があることです。

整数型のサイズを見つけ、新しいプラットフォームのリーダー/ライターでその情報を使用して、レガシープラットフォームで同じようにレイアウトされるようにする必要があります。

しかし、あなたはそのコードで本当のリスクを冒しています。そのままでは、コンパイラを変更すると、ファイルのレイアウトが突然変更される可能性があります。

于 2012-06-26T20:10:26.590 に答える
4

あなたは多くの問題にぶつかるでしょう。

C ++には、データ自体をシリアル化するための特定の形式はありません。それはあなたが実行しているコンピュータアーキテクチャ/プロセッサに大きく依存しています。

コンパイラーは、システムでの位置合わせを支援するためにパディングを追加できます。アラインメントとは、基本的に、特定のバイト境界にデータを配置するためのアーキテクチャ/プロセッサの親和性を指します。たとえば、一部のプロセッサは、浮動小数点数が4バイトまたは8バイトの境界にあることを非常に好んでいます。そうでない場合、プロセッサの動作が大幅に遅くなるか、まったく動作しない可能性があります。

したがって、システムが魔法のように追加しているパディングを単純に知ることはできません。

できることは、#pragma pack(1)/ #pragma pack(0)を使用して、コンパイラーが数値をパディングしないようにすることです。

PS:エンディアンについても心配する必要があります。1台のコンピューターがビッグエンディアンで実行されており、もう1台がリトルエンディアンである場合はどうなりますか?それらは、変換なしでバイトを異なる方法で解釈します。

簡単に言うと、適切なシリアル化スキームを使用するようにファイルを生成するアプリケーションを修正するか、特定のコンピューターで実行されているアプリケーションを確認し、ファイルの書き込み方法を正確に確認して、すべてのターゲットプラットフォームのトランスレーターを作成する必要があります。 (これはばかげています)。

興味深い提案

本当に行き詰まっている場合は、ファイルを書き込むフォルダーを監視するアプリを作成してください。アプリにファイルを取得させます(同じPC上にあるため、問題なくフォーマットを読み取ることができます)。XMLまたはその他の真のシリアル化形式でファイルを書き戻し、代わりにそれらを配布するようにします。

于 2012-06-26T20:08:11.350 に答える
4

データファイルの形式は、C ++プログラムがコンパイルされるコンパイラと、Stringクラスの定義に完全に依存します。宣言された順序でフィールドを信頼できます。この場合、最初にパディングがないことを信頼できると思いますが、これですべてです。この場合に役立つかもしれないいくつかのヒント:-

  • 使用しているStringクラスの定義は指定しません。typedefstd :: stringの場合、文字列の内容がメモリにないため、完全に失敗しています。C ++プログラマーが特別なローカルバッファーを使用していると思います。その場合、オブジェクトの最初のバイトが文字列であり、その後に無駄なパディングがいくらかあると思います。構造体の最初に、有用なデータの量を示すintが含まれていることを願っています。
  • フィールドの長さはおそらくint4バイトです。
  • boolフィールドの長さが1バイトで、その後に3バイトの役に立たないパディングが続くことに気付くでしょう。1ビットだけ、おそらく一番下のビットが設定されます。

それは私があなたに提供できるすべての有用な当て推量についてです。ターゲット言語では、ファイル全体をその言語で使用可能なバイト配列に最も近いものとして読み込むようにしてください。その後、言語機能を使用して、ファイルを言語で適切な種類のものに変換してください。C ++プログラムとは異なるエンディアンのプラットフォームを使用している場合は、バイトスワップができないため、整数として読み取ろうとしないでください。また、テキストエディタでファイルを調べてリバースエンジニアリングし、各フィールドのオフセットを見つけるのに役立てることをお勧めします。

最後のアドバイス:プログラマーやプロジェクトマネージャーがこの種の「シリアル化」が良い考えだと思った人のために、P45(またはピンクの伝票、またはあなたの国にあるもの)を印刷することを検討してください。この種のずさんな仕事は生死にかかわる状況では受け入れられたかもしれませんが、彼らはあなたが回復するのが非常に難しいと思う方法であなたを真剣に台無しにしました。これらのファイルを読み取るためのコードを書くことは、このような構造体が1つしかない場合はそれほど難しくありませんが、信頼性を維持することは苦痛の世界であり、コンパイラーやコンパイラーのバージョンを安全に変更することを事実上不可能にします。 。

于 2012-06-26T20:16:38.163 に答える
1

ofstreamに書き込むと、データはシリアル化されません。このコードは、文字列であるため、構造体の生のメモリコンテンツを書き込みます。コンパイラ、バージョン、オプション、およびコンテンツで実行されているシステムによって、完全に異なります。charのビット数でさえ、c++実装間で変更できます。構造体のオブジェクトによって参照されるデータは書き込まれません(std :: stringの内容を忘れてください)。

ライターコードを変更できない場合。アラインメントポリシー、ベースタイプのサイズ、およびデータ表現を知っている必要があります。たとえば、このような16進エディタ http://www.physics.ohio-state.edu/~prewett/hexedit/を使用して、手動で作成されたファイルを分析する必要があり ます。おそらく、コンパイラのドキュメントを参照してください。

ライターコードを変更できる場合。json、プロトコルバッファ、または単にxmlなどの適切なシリアル化を使用します。

于 2012-06-26T20:08:14.567 に答える
1

それが行われる方法で、構造体は生の形式でファイルに書き込まれます。したがって、基本的に、このファイルを解析するために知っておく必要があるのは、構造体のバイナリレイアウトです。

基本的に、フィールドは次々に配置されるため、intを読み取るには、4バイトを読み取り、それをintなどにキャストします。

文字列は特定のケースです。この「文字列」型が文字のインライン配列なのか、そのような配列へのポインタなのかは、コードからは明らかではありません。最初のケースでは、各文字列に含まれる文字数を把握し、その文字数を順番に読み取る必要があります。2番目のケースでは、文字列がファイルに書き込まれていないため、文字列を元に戻すことはできません。ポインタは役に立たないでしょう。

最後の懸念は、構造体がパックされているかどうかです。これを指定しなかったため、デフォルトでは構造体フィールドは4バイトの境界に揃えられているため、たとえばブールフィールドの後に説明する必要のあるスペースがある場合があります。構造体がパックされている場合、各フィールドは前のフィールドの直後にあります。

したがって、簡単に言うと、その定義を使用して構造体のバイナリレイアウトを把握し、他のすべてが失敗した場合は、実行時にデバッガーでメモリを検査するか、16進エディターを使用して出力ファイルを調べます。次に、その仕様をどこかに書き留めます。これにより、ファイルから読み取る必要があるものが得られます。あなたが与えた疑似定義を見ただけでは、そのレイアウトが何であるかを正確に知ることは不可能です。

于 2012-06-26T20:09:51.070 に答える
1

特に問題があると私に突き出ているものを誰も指摘していません(おそらく私がそれに噛まれていたためです)。その問題:データメンバーbool Usadl;sizeof(bool) プラットフォーム間、コンパイラ間、さらには同じコンパイラのリリース間でも異なります。の一般的な値sizeof(bool)は4と1です。これはあなたを噛みます。CHAR_BIT最近では、ビッグエンディアンのマシンを見つけるのが難しくなっています。8でない、または4でないコンピューターを見つけるのは非常に困難sizeof(int)です。これは、の場合には当てはまりませんsizeof(bool)

他のすべての人と合意して、Chadのチームはファイル内のレコードの構造を文書化し、ファイルを生成するプログラムが要素サイズ、パディング、エンディアンなど、この構造を明示的に書き込むようにする必要があります。これを行うためにクラスレイアウトに依存しないでください。それはただ問題を求めているだけです。

于 2012-06-26T21:14:08.917 に答える
0

おそらく最良の方法はJSONを使用することです。または、より堅牢なソリューションが必要な場合は、Avroなどを使用してください。AvroにはC++APIJavaAPIがあるため、発生するほとんどのケースをカバーします。

于 2012-06-26T20:07:57.823 に答える