コードからデータ構造を保存して、独自の、まだ未定義のタイプのファイルにファイルを保存するプロセスを記述しようとしています。ただし、ファイルの種類や構造を設計したことはありません。
- 一般的に言えば、デザインを始める前に考慮すべきことはありますか?
- ここで認められた良い習慣はありますか?避けるべき悪い習慣は?
- 絶対にすべきこと、すべきでないことはありますか?
コードからデータ構造を保存して、独自の、まだ未定義のタイプのファイルにファイルを保存するプロセスを記述しようとしています。ただし、ファイルの種類や構造を設計したことはありません。
回答:
最初に、作成しようとしているものに十分近いフォーマットを見つけるようにしてください。一般的に、フォーマットが必要なものよりも少し複雑に見える場合でも、独自のフォーマットを発明するよりも、誰かのフォーマットを使用する方が適切です1。
適切な既製のフォーマットが見つからない場合は、XMLやBinary XMLなどの既存の汎用フォーマットの上に独自のフォーマットを構築できるかどうかを確認してください。これは、新しいファイル形式を開始しようとしているほとんどすべての場合に可能です。テキストベースのXMLはより多くのスペースを必要としますが、人間にはある程度の読みやすさを与えます。ただし、XMLファイル内でBase-64エンコーディングを使用している場合は、代わりにバイナリエンコーディングを使用する必要があったことは明らかです。
良い方法と悪い方法の範囲では、最初のターゲットプラットフォームのハードウェア機能をファイル形式の設計に「焼き付け」ないようにしてください。具体的には、数値が、ライターのエンディアンとは異なるエンディアンのプラットフォームで正しく読み取れる形式で格納されていること、およびユーザー向けの文字列がUNICODEで格納されていることを確認してください。
別の良い方法は、拡張子が見つからないか正しくない場合にファイルのタイプを判別できるヘッダーを含めることです。ヘッダーにファイル形式のバージョンを含めることをお勧めします。これにより、後でフォーマットを変更し、下位互換性を保つことができます。
可能であれば、プラットフォームに組み込まれているデフォルトのシリアル化メカニズムの詳細に依存した形式にしないでください。たとえば、バイナリシリアル化されたJavaオブジェクトは適切なファイル形式にはなりません2。
最後に、ファイルをストリーミング可能にする必要があるかどうかを判断します。これにより、ファイルの個々の「フレーム」を分離して解釈できるため、さらに複雑になります。ただし、ストリーミング性が必要な場合は、ほとんどの場合、既存の適切なファイル形式を見つけることができるはずです。
2ただし、これは、新しい形式の読み取りと書き込みをプラットフォームのシリアル化スキームとカスタム統合しようとするべきではなく、デフォルトのシリアル化メカニズムに依存すべきではないという意味ではありません。
最初に検討すべきことは、実際に新しいフォーマットが必要かどうか、または既存のフォーマットを使用して取得できるかどうかです。SQLiteの使用を検討してください。RDBMSモデルに適合するようにニーズを適合させることができれば、これにより多くの頭痛の種を減らすことができます。また、XMLまたはJSONの使用を検討してください。これにより、独自のパーサーを作成する必要がなくなります。
独自の形式を作成する必要がある場合、最初の考慮事項は、テキスト形式が必要かバイナリ形式が必要かです。両方に利点があります。テキスト形式は移植性にとって大きな利点であり、人間が読みやすく編集しやすいという利点があります。バイナリ形式の方が効率的ですが、それに伴う移植性の問題がたくさんあります。変数にバイトを直接読み取るように誘惑されないでください。コードを別のプラットフォームに移植する必要がある場合は、後悔します。
最初の最も重要な決定は、バイナリ形式を使用するか、テキストベースの形式を使用するかです。バイナリは、文字列以外のデータを大量にダンプする必要がある場合に使用する方法です。しかし、それには重大な欠点があります。
バイナリデータは人間が読める形式ではありません。そのため、すでにディスク上にあるデータのデバッグや調整が非常に困難になります。これが、UNIXの哲学がテキストベースのファイルを非常に強く採用する理由の1つです。
バイナリ形式は、将来の拡張に向いていません。これは可能ですが、拡張性のポイントを最初からフォーマットに組み込む必要があります。通常、これらは
フォーマットを識別するマジックナンバー/文字列
フォーマットのバージョン番号
ゼロに初期化する必要のある戦略的位置の予約フィールド
通常、最初の2つはファイルの先頭に表示されますが、予約フィールドは通常ファイル全体に散在しています。
ここで、テキストベースのルートを使用する場合、以下の点を考慮してください。
テキストベースのフォーマットは、新しいミニ言語を定義します。これを知って、あなたの利益のためにそれを使ってください。
ミニ言語のルールはできるだけシンプルにしてください。テキストベースのファイル形式を設計するときほど、KISSの原則が重要な場所はありません。
ファイルをわかりやすく説明してください。
空白が表示される場所やその量など、不要な制限を課さないでください。
UNIX用に開発されたさまざまなファイル形式をよく見てください。これはあなたにかなりの数の良いアイデアを与えることができます。
可能であれば、既存のファイル形式を使用または適応/拡張/制約します。JSONの形式は非常に読みやすい、良い出発点です。(少なくとも、XMLよりはるかに優れています。XMLは人間にとって読みにくいものです。)
ファイルサイズが問題になる場合は、とにかくテキストベースのフォーマットを使用することを検討しますが、gzip
またはなどの標準的なコンプレッサーの1つに渡しlzma
ます。標準的なコンプレッサーは、そのような入力を好みます。
バイナリルートを使用する場合は、次の点に注意してください。
マジックナンバー/文字列とバージョン番号を含むヘッダーが必要です。通常、これはファイルの先頭に移動しますが、ファイルの最後に移動することもあります。一部のファイルでは、表と裏に2つの異なるヘッダーがあり、内部のデータを2つの独立したビューで表示します。
インデックスが必要です。また、そのパーツを互いに近づけるようにしてください。これにより、全体をスキャンしなくても、ファイル内の内容をすばやく見つけることができます。そうしないと、すべてを2回読むことになります。
インデックス構造ではなくシーケンスとしてのみアクセスできるファイルのビットがある場合は、シーケンス内の各レコードに少なくとも長さフィールドを含めます。インデックスまたはそのような長さフィールドは、フォーマットのすべての詳細を理解しておらず、その一部をブラックボックスとしてスキップする必要があるリーダーにとって必要条件です。(これについてはJulesに感謝します。)
ファイル内のすべてのデータオブジェクトには、将来の拡張のために少なくとも1つの予約フィールドを含める必要があります。これは大きい必要はありませんが、そこにある必要があります。そうでないと、将来の機能を認識できる場所がないからです。
エンディアンを考慮する必要があります。通常、これは、ファイルをビッグエンディアンまたはリトルエンディアンのバイトオーダーでエンコードするかどうかを一度決定し、その決定に固執することを意味します。ファイルのエンディアネスの。
提供するフィールドの幅に寛大であること。特に、ファイル内のオフセットをエンコードする必要がある場合は、常に64ビットを使用してください。多くの頭痛の種は、ファイル形式の設計者が割り当てたビット数に慎重すぎるために発生しました。
それは本当にあなたが何をしているかに依存します。それはできるだけ単純でなければならず、単純ではありません。他の多くの人々がXMLを推進しているのを見ます。XMLの使用はお勧めしません。XMLは過度に指定された混乱です。最初の質問は、データ構造にブランチがあるかどうかです。それらはリストのリストまたはマップのリストなどの意味ですか?いいえの場合、テキストレコードの単純なシーケンスが適切な場合があります。たぶんCSV。
パフォーマンスやランダムアクセスが必要な場合は、バイナリが適しています。レコードのシーケンスを定義します。各レコードには、ある数の4バイトリトルエンディアン整数またはUTF-8文字列のバイト数を指定する2バイトの整数のような特定のサイズのデータムのシーケンスが含まれます。各レコードは、レコードのサイズを指定する整数で始まるようにして、実際にレコードの内容を読み取らずにファイル全体をスキャンできるようにします。これにより、その場でレコードをエンコードすることもできます(つまり、ファイルをmmap、レコードをエンコードし、後でサイズを更新して、不要なコピーを最小限に抑えることができます)。これは、XMLではできないことです。