45

バイナリ データを記録するためのファイル形式を設計する場合、その形式にはどのような属性が必要だと思いますか? これまでのところ、次の重要なポイントを考え出しました。

  • ファイルを認識できるように、最初にいくつかの「マジックバイト」があります(私の特定のケースでは、これはファイルを「レガシー」ファイルと区別するのにも役立ちます)
  • 互換性を損なうことなくファイル形式を後で変更できるように、最初にファイルのバージョン番号を付けます。
  • すべてのデータ項目のエンディアンとサイズを指定します。または:データのエンディアン/サイズを説明するスペースを含めます(私は前者に傾向があります)
  • 将来必要になる可能性のあるファイルごとの属性用にスペースを確保する可能性はありますか?

フォーマットをより将来性のあるものにし、将来の頭痛の種を最小限に抑えるために他に何が役立つでしょうか?

4

10 に答える 10

27

PNG仕様を見てください。この形式には、その背後にある非常に優れた理論的根拠があります。

また、将来のフォーマットにとって何が重要かを決定します。つまり、コンパクトさ、互換性、他のフォーマット (異なる圧縮アルゴリズム) を内部に埋め込むことができるようにすることです。もう 1 つの興味深い例は、Google のプロトコル バッファです。ここでは、転送されるデータのサイズが重要です。

エンディアンについては、1 つのオプションを選択してそれを使い続けることをお勧めします。異なるバイト オーダーは許可されません。そうしないと、ライブラリの読み取りと書き込みがより複雑になり、遅くなるだけです。

于 2008-11-27T12:38:30.880 に答える
18

これらが良いアイデアであることに同意します。

  1. 冒頭のマジックナンバー。*nix ではかなり必要:

  2. 下位互換性のためのファイル バージョン番号。

  3. エンディアン仕様。

しかし、バージョン番号を変更する限り(および前方互換性が必要ない限り)#2ではフィールドを追加できるため、4番目のものはやり過ぎです。

  • 将来必要になる可能性のあるファイルごとの属性用にスペースを確保する可能性はありますか?

また、他の多くの回答で表現されているファイルにブロック構造を課すという考えは、特定の種類のペイロードに関する問題の解決策よりも、バイナリ ファイルの普遍的な要件のようには思えません。

上記の 1 ~ 3 に加えて、これらを追加します。

  • 単純なチェックサムまたはコンテンツが無傷であることを検出するその他の方法。そうしないと、マジック バイトやバージョン番号を信頼できません。チェックサムに含まれるバイトを指定するように注意してください。通常、まだエラーが検出されていないすべてのバイトをファイルに含めます。

  • ファイルを書き込んだソフトウェアのバージョン (ビルド番号など、最も詳細な番号を含む)。添付ファイルが添付されたバグ レポートを、そのファイルを開くことができない人から受け取りますが、エラーは発生しなかったため、いつファイルを書き込んだのかわかりません。しかし、バグはそれを読み取ろうとしているバージョンではなく、それを書いたバージョンにあります。

  • これがバイナリ形式であることを仕様で明確にしてください。つまり、すべてのバイトに 0 ~ 255 のすべての値を使用できます (マジック ナンバーを除く)。

そして、ここにいくつかのオプションがあります:

  • 前方互換性が必要な場合は、どの「チャンク」が「オプション」であるか (png のように) を表現する何らかの方法が必要です。これにより、以前のバージョンのソフトウェアがそれらを適切にスキップできるようになります。

  • これらのファイルが「野生で」見つかると予想される場合は、仕様を見つけるための手がかりを埋め込むことを検討してください。png ファイル内の文字列http://www.w3.org/TR/PNG/を見つけることがどれほど役立つか想像してみてください。

于 2008-12-29T17:35:42.587 に答える
11

もちろん、それはすべてフォーマットの目的に依存します。

柔軟なアプローチの 1 つは、ファイル全体を TLV (Tag-Length-Value) トリプレットとして構造化することです。たとえば、ファイルをレコードで構成し、各レコードが 4 バイトのヘッダーで始まるようにします。

1 byte  = record type
3 bytes = record length
followed by record content

エンディアンに関しては、ファイルにエンディアン インジケーターを保存する場合、すべてのアプリケーションがすべてのエンディアン形式をサポートする必要があります。一方、ファイルに特定のエンディアンを指定すると、エンディアンが一致しないプラットフォーム上のアプリケーションのみが追加の作業を行う必要があり、コンパイル時に決定できます (条件付きコンパイルを使用)。

于 2008-11-27T12:47:06.120 に答える
7

.xz ファイル仕様 ( http://tukaani.org/xz/xz-file-format.txt ) からの別のポイント: アプリケーションがファイルを誤検出するのを防ぐために、最初の数バイトの 1 つは文字以外にする必要があります。テキストファイルとして。」通常、エディターやその他のツールによってヘッダー バイト数が検査されることに注意してください。ただし、最初の 4 バイトまたは 8 バイトに非バイナリ バイトを使用すると便利なようです。

于 2012-05-15T11:42:19.420 に答える
3

削除/解放されたブロック/チャンクを指定するタグコードを予約する(または、各タグにビットを予約する方がよい)ことを確認してください。ブロックは、ブロックの現在のタグコードを削除されたタグコードに変更するか、タグの削除されたビットを設定するだけで削除できます。このように、ブロックを削除するときにファイルをすぐに完全に再構築する必要はありません。

タグのビットを予約すると、ブロックの削除を取り消すオプションが提供されます(ブロックのデータを変更しない場合)。

ただし、セキュリティのために、削除されたブロックのデータをゼロにすることができます。この場合、特別な削除済み/解放タグを使用します。

エンディアンを選択する必要があるというStepanに同意しますが、ファイルにはエンディアンインジケーターも含まれます。エンディアンインジケーターを使用する場合は、UniCodeバイトオーダーマークの1つを、テキストブロックに使用されるUniCodeテキストエンコーディングのインジケーターとしても使用することを検討してください。BOMは通常UniCodedテキストファイルの最初の数バイトであるため、BOMがファイルの最初のエントリである場合、ユーティリティがファイルをUniCodeテキストとして識別する問題が発生する可能性があります(これはそれほど問題ではないと思います) 。BOMを通常のタグの1つとして扱い/予約します(16ビットタグを使用する場合はUTF16 BOMを使用し、32ビットタグを使用する場合はUTF32 BOMを使用します)。ブロック/チャンクの長さは0です。

http://en.wikipedia.org/wiki/File_formatも参照してください

于 2008-12-02T21:09:03.063 に答える
3

ファイル内のミニファイルシステムのような、より高いレベルでデータを保存するために使用するサブ構造を定義することを検討します。

たとえば、ファイル形式がアプリケーション固有のデータを保存する場合でも、アプリケーションに依存しないコードがファイルのレイアウトを理解できるように、ファイル内にレコード/ストリームなどを定義することを検討しますが、もちろん、不透明なペイロードを理解してください。

もう少し具体的に見てみましょう。メモリにデータを格納する通常の方法を検討してください。一般に、それらは、連続した展開可能な配列/リスト、ポインター/参照ベースのグラフ、および特定の形式のデータのバイナリ BLOB に要約できます。

したがって、同様の方針に沿ってバイナリ ファイル形式を定義すると有益な場合があります。配列 (同じ型のレコードのリスト)、参照 (ファイル内の他のレコードへのオフセット)、またはデータ BLOB (文字列データなど) の形式であるかどうかにかかわらず、次のデータの長さと構成を示すレコード ヘッダーを使用します。特定のエンコーディングで、しかし参照を含まない)。

慎重に設計されていれば、ファイル形式を一度にデータを永続化するためだけでなく、必要に応じて段階的に使用することもできます。サブ構造が適切に設計されている場合、アプリケーションにとらわれず、たとえば、ブロブ、配列、参照レコードの種類を理解し、ファイルをトレースして未使用のレコード (つまり、レコード) を削除できるガベージ コレクション アプリケーションを作成できます。指されなくなったもの)。

それはただのアイデアです。アイデアを探す他の場所は、一般的なファイル システム設計、またはリレーショナル データベースの物理ストレージ戦略です。

もちろん、要件によっては、これはやり過ぎかもしれません。メモリ内データを永続化するためのバイナリ形式を単に求めているだけかもしれません。その場合、考慮すべきアプローチはタグ付けされたレコードです。

このアプローチでは、すべてのデータにタグのプレフィックスが付けられます。タグは、直後のデータのタイプと、場合によってはその長さと名前を示します。リストには、ペイロードのない「end-list」タグをサフィックスとして付けることができます。タグには識別子が埋め込まれている可能性があるため、理解されていないタグは、シリアル化メカニズムが内容を読み取るときに無視できます。この点では、代わりにバイナリ イディオムを使用することを除いて、XML に少し似ています。

実際、XML は、ファイル形式の長期的な寿命を探すのに適した場所です。その名前空間機能を見てください。読み取りと書き込みのコードを慎重に作成すれば、タグ付けされた (再帰的に) データが理解できない場所と内容を保持するアプリケーションを作成できるはずです。

于 2008-11-27T12:45:26.810 に答える
3

ファイルを将来証明する 1 つの方法は、ブロックを提供することです。ファイル ヘッダー データの直後に、最初のブロックを開始できます。ブロックには、ブロックのタイプのバイトまたはワード コードと、バイト単位のサイズがあります。新しいブロック タイプを任意に追加できるようになり、ブロックの最後までスキップできるようになりました。

于 2008-11-27T12:46:08.523 に答える
2

タグ長値システムを使用する atzz の提案に同意します。将来の互換性のために、最初に TLV エントリへの「ポインタ」のセットを保存できます (または、タグ、ポインタでポインタが長さ、値を指すようにするか、またはタグ、長さ、ポインタですべてのデータをまとめることができます)。他に?)。

したがって、私のファイルは次のようになります。

magic number/file id
version
tag for first data entry
pointer to first data entry --------+
tag for second data entry           |
pointer to second data entry        |
...                                 |
length of first data entry <--------+
value for first data entry
...

マジック ナンバー、バージョン、タグ、ポインタ、および長さはすべて、簡単にデコードできるように事前定義された設定長になります。2 バイトと言います。または、必要に応じて 4 つ。すべてが同じである必要はありません (たとえば、すべてのタグが 1 バイト、ポインターが 4 バイトなど)。

タグにより、何が格納されているかがわかります。ポインターは場所 (バイト単位のオフセットまたは絶対値) を示し、長さはデータの大きさを示し、tag型のデータの長さバイトです。MyFileFormat v2 ファイルで MyFileFormat v1 デコーダーを使用する場合、ポインターを使用すると、v1 デコーダーが理解できないセクションをスキップできます。無効なタグを単純にスキップする場合は、おそらく TPLV の代わりに TLV を使用できます。

私はそのようなものを手作業でコーディングするか、ASN.1でフォーマットを定義してコーデックを生成します(私は電気通信で働いているので、ASN.1/TLVは私にとって理にかなっています:-D)

于 2008-11-27T13:34:55.430 に答える
2

可変長データを扱っている場合は、ポインターを使用する方がはるかに効率的です。データを配列に直接格納するのではなく、データへのポインターの配列を、理想的にはファイルの先頭近くに配置します。

この例では、すべての項目が同じサイズである場合にのみ可能なランダムアクセスが可能になるため、間接化が推奨されます。データがレコードの場所を指定せずに配列に直接格納された場合、最悪の場合、データ アクセスに O( n ) の時間がかかります。ファイルを読み取るコードが特定の要素にアクセスするには、以前のすべての要素の長さを知る必要があり、それを見つける唯一の方法は、それぞれを調べることです。ファイル全体を一度に読み取る場合は、とにかくこれを行うため、問題にはなりません。しかし、1 つのことだけが必要な場合、これは適切な方法ではありません。

一方、ポインターの配列では、全体的に O(1) 時間です。必要なのはインデックス番号だけであり、ポインターを取得してたどってデータを取得することができます。

このメソッドを使用してファイルを書き込む場合、もちろん、書き込みを行う前にメモリ内にテーブルを構築する必要があります。

于 2012-11-19T01:28:21.893 に答える