C での変更を保持する
-
27-09-2019 - |
質問
私は、以下を含む構造を格納するデータベースのようなアプリケーションを開発しています。
struct Dictionary
{
char *key;
char *value;
struct Dictionary *next;
};
ご覧のとおり、情報を保存するためにリンク リストを使用しています。しかし、問題はユーザーがプログラムを終了したときに始まります。情報をどこかに保存しておきたい。そこで、fopenを使用してリンクリストを永続ファイルまたは一時ファイルに保存し、ユーザーがプログラムを起動したときにリンクリストを取得することを考えていました。リンクされたリストをコンソールに出力するメソッドは次のとおりです。
void PrintList()
{
int count = 0;
struct Dictionary *current;
current = head;
if (current == NULL)
{
printf("\nThe list is empty!");
return;
}
printf(" Key \t Value\n");
printf(" ======== \t ========\n");
while (current != NULL)
{
count++;
printf("%d. %s \t %s\n", count, current->key, current->value);
current = current->next;
}
}
そこで、このメソッドを変更して、printf の代わりに fprintf を通じて情報を出力し、プログラムがファイルから情報を取得するだけになるように考えています。誰かがこのファイルを読み書きする方法を教えてくれませんか?一時ファイルか通常ファイルか、どのような種類のファイルにする必要がありますか?ファイルをどのようにフォーマットすればよいでしょうか (最初にキー、次に値、そして改行文字を考えていたように)
解決
ファイルは、おそらく定期的にする必要があります。一時ファイルは、そこにあなたのアプリケーションを起動し、次の時間であることが保証されていません。また、あなたのフォーマットはありませんので、細かいマシンについて、人間のための罰金が見えます。私は自分自身のバイナリファイル形式を作成したり、XMLのいずれかを使用してお勧めします(または多分JSONを?)。あなたは、おそらくかなり簡単のようにそれをフォーマットすることができます。
key1\0value1\0key2\0value2\0....
私は簡単な例を書きますがpsuedoishコードです。
//To write...
Dictionary *this=begin_list;
while(this!=null){
for(i=0;i<strlen(this->key);i++){
write_byte(this->key[i]);
}
for(i=0;i<strlen(this->value);i++){
write_byte(this->value[i]);
}
this=this->next;
}
//to read...
Dictionary *prev;
Dictionary *this;
char *buffer;
while(!eof){
buffer=malloc(MAX_STRING_LEN);
int i=0;
this=malloc(sizeof(Dictionary)
while(i<MAX_STRING_LEN){ //note no error checking
buffer[i]=read_byte();
if(buffer[i]==0){
break;
}
}
this->key=buffer;
buffer=malloc(MAX_STRING_LEN)
while(i<MAX_STRING_LEN){ //note no error checking
buffer[i]=read_byte();
if(buffer[i]==0){
break;
}
}
this->value=buffer;
if(prev!=null){
prev->next=this;
}
this->next=null;
prev=this;
}
私はそれが悪い例です知っています。私はscanf関数または類似のトンより簡単に仕事をすることができると思うが、私のCのスキルが錆びてきています。
他のヒント
根本的な問題は、ポインタが外部ストレージに変換されないことです。プログラムを再度実行したときに、同じメモリ範囲 (アドレス) が設定されるという保証はありません。この原則を考慮すると、データを保存する別の方法があります。
永続データのプロセス:
1.大小を問わずデータベースを使用します。
2.データをスキャン可能な形式の ASCII テキストに変換します。
3.固定長のバイナリ レコードを使用する
4.可変サイズのバイナリ レコードを使用する
5.ポインターの代わりにファイル オフセットを使用して辞書データ構造を実装します。
データベースの使用
専門的なアプリケーション (テスト済みで動作するもの) にデータを管理させましょう。これにより、データの保存や取得ではなく、データの使用に集中できます。
スキャン可能な形式に変換する
ここでの考え方は、取得と保守が容易な形式でデータをファイルに書き込むことです。例には、カンマ区切り値 (CSV)、XML、INI などがあります。これには、データの読み取りと書き込みを行うためのコードが必要です。役立つライブラリがあります。
固定長のバイナリ レコードを使用する
固定長レコードの場合、データはファイルから読み取られて辞書に挿入されます。バイナリ ファイルは、データの転送に関しては非常に効率的ですが、特にオペレーティング システムのバージョンが変更された場合、プラットフォームが変更された場合、またはコンパイラのバージョンが変更された場合は、移植性があまり高くありません。テキスト レコード用のスペースが無駄になる可能性があります。
可変サイズのバイナリ レコードを使用する
この手法によりスペースは節約されますが、処理時間は長くなります。次のレコードの場所を見つけるには、各レコードを処理する必要があります。レコードへのランダムアクセスは困難です。それ以外は固定長バイナリ レコードと同様です。
ファイルに辞書データ構造を実装します。
ポインタの代わりにファイル オフセットを使用することを除いて、メモリ ベースのデータ構造と同じアルゴリズムです。新しいレコードをファイルの末尾に追加できます。削除されたエントリを再利用することは困難であり、断片化につながります。断片化は新しいファイルを書き込むことで解決できます。これだけの労力を費やすのであれば、既存のデータベース アプリケーションを使用した方がよいでしょう。
は、あなたが読み取りまたはファイルへの書き込みができ一つの方法は、次のようにfreopenは使用しています: freopenは(「file.out」、「重量」、標準出力)、その後、あなたはしているのprintfのfile.outに行くと、あなたはたくさんのコードを変更する必要はありません。
あなたはテキスト形式で情報を保存することができますが、私は実際にこれを行うための最善の方法は、バイナリファイル内の情報の保存だと思います。 あなたは、関数freadとfwriteのについては、この検索情報の詳細をチェックアウトすることができます。
この問題を解決する 1 つの方法があります。
次のようにリスト項目のデータ構造を作成します。
struct DictionaryArchive {
char key[MAX_KEY_LENGTH];
char value[MAX_VALUE_LENGTH];
int next;
};
の値を決定する必要があります。 MAX_KEY_LENGTH
そして MAX_VALUE_LENGTH
あなたが期待するデータによると。
ここで、リンクされたリストをこれらの構造の配列に変換します。次の項目を見つけるためのポインターを保存する代わりに、次の項目の配列インデックスを保存します。これにより、リストが、各要素が予測可能なサイズになる形式に変換され、リスト全体が 1 つの連続したメモリ スパンになります。さあ、できるようになりました fwrite
この配列をバイナリ ファイルに変換してアーカイブし、 fread
復元するには元に戻します。
固定サイズを使用するよりもはるかにスペース効率の高い代替手段 char
上記の配列では、静的構造を使用する代わりにカスタム ファイル形式を定義します。あなたの場合、次のようなファイル形式を使用して、データを取得可能な方法で保存できます。
- リストは、先頭から始まり、次の順にファイルに書き込まれます。
next
尻尾へのポインタ - 各リスト項目は、4 つのデータ フィールドを次の順序で使用して保存されます。
- 16ビット整数、
key_length
- 8 ビット char 配列
key_length
要素、key_data
- 16ビット整数、
value_length
- 8 ビット char 配列
value_length
要素、value_data
- 16ビット整数、
これで、リストをたどってノードごとにデータをファイルにダンプできるようになります。データを再構築するには、バイナリ ファイルを読み取り、新しいファイルを生成します。 struct Dictionary
各エントリの要素を作成し、ファイル内に表示される順序でそれらをリンクします。
データをデータ ファイルに書き込むコードは次のようになります (未テスト、説明のみを目的としています)。
FILE* fd;
size_t len;
struct Dictionary* pDict = list_head;
fd = fopen("output_file.dat", "w");
// Walk through the list, storing each node
while (pDict != NULL) {
// Store key
len = strlen(pDict->key);
fwrite(&len, sizeof(len), 1, fd);
fwrite(pDict->key, len, sizeof(char), fd);
// Store value
len = strlen(pDict->value);
fwrite(&len, sizeof(len), 1, fd);
fwrite(pDict->value, len, sizeof(char), fd);
// Move to next list node
pDict = pDict->next;
};
fclose(fd);
データを読み出すコードは非常に似ています (書き込みの代わりに読み取り、新しいデータを作成します)。 struct Dictionary
各ループ反復のオブジェクト)。