ランダム アクセス ファイル処理に関する C プログラミング チュートリアル
ロイ・スコット/ゲッティイメージズ
最も単純なアプリケーションを除けば、ほとんどのプログラムはファイルを読み書きする必要があります。構成ファイル、テキストパーサー、またはより洗練されたものを読み取るためだけかもしれません。このチュートリアルでは、C でのランダム アクセス ファイルの使用に焦点を当てています。
C でのランダム アクセス ファイル I/O のプログラミング
D3Damon/ゲッティイメージズ
基本的なファイル操作は次のとおりです。
- fopen - ファイルを開きます - ファイルを開く方法 (読み取り/書き込み) とタイプ (バイナリ/テキスト) を指定します
- fclose - 開いているファイルを閉じる
- fread - ファイルから読み取る
- fwrite - ファイルに書き込みます
- fseek/fsetpos - ファイル ポインタをファイル内のどこかに移動する
- ftell/fgetpos - ファイル ポインタの場所を教えてくれます
2 つの基本的なファイル タイプは、テキストと バイナリ。 通常、これら 2 つのファイルのうち、バイナリ ファイルの方が扱いが簡単です。この理由と、テキスト ファイルへのランダム アクセスは頻繁に行う必要がないという事実から、このチュートリアルはバイナリ ファイルに限定されています。上記の最初の 4 つの操作は、テキスト ファイルとランダム アクセス ファイルの両方を対象としています。最後の 2 つはランダム アクセス用です。
ランダム アクセスとは、ファイル全体を読み取らなくても、ファイルの任意の部分に移動してデータを読み書きできることを意味します。数年前、データはコンピュータ テープの大きなリールに保存されていました。テープのポイントにたどり着く唯一の方法は、テープを最後まで読むことでした。その後、ディスクが登場し、ファイルの任意の部分を直接読み取ることができるようになりました。
バイナリ ファイルを使用したプログラミング
バイナリ ファイルは、0 ~ 255 の範囲の値を持つバイトを保持する任意の長さのファイルです。これらのバイトは、13 の値がキャリッジ リターンを意味し、10 が改行を意味し、26 が末尾を意味するテキスト ファイルとは異なり、他の意味を持ちません。ファイル。テキスト ファイルを読み取るソフトウェアは、これらの他の意味に対処する必要があります。
バイナリ ファイルはバイト ストリームであり、最近の言語はファイルではなくストリームで動作する傾向があります。重要な部分は、データ ストリームがどこから来たかではなく、データ ストリームです。のCの場合、データはファイルまたはストリームとして考えることができます。ランダム アクセスを使用すると、ファイルまたはストリームの任意の部分に対して読み取りまたは書き込みを行うことができます。シーケンシャル アクセスでは、大きなテープのように最初からファイルまたはストリームをループする必要があります。
このコード サンプルは、単純なバイナリ ファイルが書き込み用に開かれ、テキスト文字列 (char *) が書き込まれていることを示しています。通常、これはテキスト ファイルで表示されますが、テキストをバイナリ ファイルに書き込むこともできます。
この例では、書き込み用にバイナリ ファイルを開き、そこに char * (文字列) を書き込みます。 FILE * 変数は、fopen() 呼び出しから返されます。これが失敗した場合 (ファイルが存在し、開いているか読み取り専用であるか、ファイル名に問題がある可能性があります)、0 を返します。
fopen() コマンドは、指定されたファイルを開こうとします。この場合、アプリケーションと同じフォルダーにある test.txt です。ファイルにパスが含まれている場合は、すべてのバックスラッシュを 2 つにする必要があります。 「c:folder est.txt」は正しくありません。 「c:\folder\test.txt」を使用する必要があります。
ファイル モードが「wb」であるため、このコードはバイナリ ファイルに書き込みます。ファイルが存在しない場合は作成され、存在する場合はその中にあったものはすべて削除されます。ファイルが開いていたか、名前に無効な文字または無効なパスが含まれているなどの理由で fopen の呼び出しが失敗した場合、fopen は値 0 を返します。
ft がゼロでないこと (成功) をチェックすることもできますが、この例にはこれを明示的に行うための FileSuccess() 関数があります。 Windows では、呼び出しの成功/失敗とファイル名を出力します。パフォーマンスを求めている場合は少し面倒なので、これをデバッグに制限することができます。 Windows では、システム デバッガーにテキストを出力するオーバーヘッドはほとんどありません。
fwrite() 呼び出しは、指定されたテキストを出力します。 2 番目と 3 番目のパラメーターは、文字のサイズと文字列の長さです。どちらも、符号なし整数である size_t として定義されています。この呼び出しの結果、指定されたサイズの count 個のアイテムが書き込まれます。バイナリ ファイルでは、文字列 (char *) を書き込んでいても、改行文字や改行文字は追加されないことに注意してください。それらが必要な場合は、文字列に明示的に含める必要があります。
ファイルを読み書きするためのファイル モード
ファイルを開くときは、ファイルを開く方法 (新規から作成するか上書きするか、テキストかバイナリか、読み取りか書き込みか、ファイルに追加するか) を指定します。これは、1 文字の 'r'、'b'、'w'、'a'、および '+' を他の文字と組み合わせた 1 つ以上のファイル モード指定子を使用して行われます。
- r - ファイルを読み取り用に開きます。ファイルが存在しないか見つからない場合、これは失敗します。
- w - 書き込み用にファイルを空のファイルとして開きます。ファイルが存在する場合、その内容は破棄されます。
- a - 新しいデータをファイルに書き込む前に EOF マーカーを削除せずに、ファイルの末尾に書き込む (追加する) ためにファイルを開きます。これにより、ファイルが存在しない場合に最初に作成されます。
ファイル モードに「+」を追加すると、3 つの新しいモードが作成されます。
- r+ - 読み取りと書き込みの両方のためにファイルを開きます。 (ファイルが存在している必要があります。)
- w+ - ファイルを読み取りと書き込みの両方で空のファイルとして開きます。ファイルが存在する場合、その内容は破棄されます。
- a+ - 読み取りと追加のためにファイルを開きます。追加操作では、新しいデータがファイルに書き込まれる前に EOF マーカーが削除され、書き込みが完了した後に EOF マーカーが復元されます。ファイルが存在しない場合は、最初にファイルを作成します。読み取りと追加のためにファイルを開きます。追加操作では、新しいデータがファイルに書き込まれる前に EOF マーカーが削除され、書き込みが完了した後に EOF マーカーが復元されます。ファイルが存在しない場合は、最初にファイルを作成します。
ファイル モードの組み合わせ
この表は、テキスト ファイルとバイナリ ファイルの両方のファイル モードの組み合わせを示しています。通常、テキスト ファイルの読み取りまたは書き込みのいずれかを行いますが、両方を同時に行うことはありません。バイナリ ファイルを使用すると、同じファイルに対して読み取りと書き込みの両方を行うことができます。下の表は、各組み合わせで何ができるかを示しています。
- r テキスト - 読む
- rb+ バイナリ - 読み取り
- r+ テキスト - 読み取り、書き込み
- r+b バイナリ - 読み取り、書き込み
- rb+ バイナリ - 読み取り、書き込み
- w text - 書き込み、作成、切り捨て
- wb バイナリ - 書き込み、作成、切り捨て
- w+ text - 読み取り、書き込み、作成、切り捨て
- w+b バイナリ - 読み取り、書き込み、作成、切り捨て
- wb+ バイナリ - 読み取り、書き込み、作成、切り捨て
- テキスト - 書く、作成する
- ab バイナリ - 書き込み、作成
- a+ テキスト - 読み取り、書き込み、作成
- a+b バイナリ - 書き込み、作成
- ab+ バイナリ - 書き込み、作成
ファイルを作成するだけ ('wb' を使用) または 1 つだけを読み取る ('rb' を使用) 場合を除き、'w+b' を使用して問題を解決できます。
一部の実装では、他の文字も許可されます。 マイクロソフト 、たとえば、次のことができます。
- t - テキストモード
- c - コミット
- n - コミットしない
- S - シーケンシャル アクセスのキャッシングの最適化
- R - 非順次キャッシング (ランダムアクセス)
- T - 一時的
- D - 削除/一時、ファイルが閉じられたときにファイルを強制終了します。
これらは移植性がないため、自己責任で使用してください。
ランダム アクセス ファイル ストレージの例
バイナリ ファイルを使用する主な理由は、ファイル内の任意の場所で読み書きできる柔軟性にあります。テキスト ファイルでは、シーケンシャルに読み書きすることしかできません。次のような安価または無料のデータベースの普及により、 SQLite とMySQL、バイナリ ファイルでランダム アクセスを使用する必要性を減らします。ただし、ファイル レコードへのランダム アクセスは少し時代遅れですが、それでも便利です。
例を調べる
この例では、ランダム アクセス ファイルに文字列を格納するインデックス ファイルとデータ ファイルのペアを示しているとします。文字列の長さは異なり、位置 0、1 などでインデックスが付けられます。
CreateFiles() と ShowRecord(int recnum) の 2 つの void 関数があります。 CreateFiles は、サイズ 1100 の char * バッファーを使用して、フォーマット文字列 msg とそれに続く n 個のアスタリスク (n は 5 から 1004 の範囲) で構成される一時文字列を保持します。作成後、これらはファイルの操作に使用されます。 2つのファイルは
- index.dat
- データ.dat
インデックス ファイルには、タイプ indextype の 1000 レコードが保持されます。これは struct indextype で、pos (型 fpos_t) と size の 2 つのメンバーを持ちます。ループの最初の部分:
このように文字列 msg を入力します。
等々。次に、これ:
文字列の長さと、文字列が書き込まれるデータ ファイル内のポイントを構造体に入力します。
この時点で、インデックス ファイル構造体とデータ ファイル文字列の両方をそれぞれのファイルに書き込むことができます。これらはバイナリ ファイルですが、順番に書き込まれます。理論的には、現在のファイルの終わりを超えた位置にレコードを書き込むことができますが、これは使用するのに適した手法ではなく、おそらく移植性もまったくありません。
最後の部分は、両方のファイルを閉じることです。これにより、ファイルの最後の部分が確実にディスクに書き込まれます。ファイルの書き込み中、書き込みの多くは直接ディスクに書き込まれるのではなく、固定サイズのバッファーに保持されます。書き込みによってバッファーがいっぱいになると、バッファーの内容全体がディスクに書き込まれます。
ファイル フラッシュ関数はフラッシュを強制し、ファイル フラッシュ戦略を指定することもできますが、これらはテキスト ファイルを対象としています。
ShowRecord 関数
データ ファイルから特定のレコードを取得できることをテストするには、2 つのことを知る必要があります。それは、データ ファイル内の開始位置と、その大きさです。
これがインデックスファイルの機能です。 ShowRecord 関数は両方のファイルを開き、適切なポイント (recnum * sizeof(indextype)) をシークし、バイト数 = sizeof(index) をフェッチします。
SEEK_SET は、どこから fseek を実行するかを指定する定数です。このために、他に 2 つの定数が定義されています。
- SEEK_CUR - 現在の位置を基準にしてシークする
- SEEK_END - ファイルの最後から絶対シーク
- SEEK_SET - ファイルの先頭から絶対シーク
SEEK_CUR を使用して、ファイル ポインタを sizeof(index) だけ前方に移動できます。
データのサイズと位置を取得したら、あとは取得するだけです。
ここでは、index.pos の型が fpos_t であるため、fsetpos() を使用します。別の方法は、fgetpos の代わりに ftell を使用し、fgetpos の代わりに fsek を使用することです。 fseek と ftell のペアは int で動作しますが、fgetpos と fsetpos は fpos_t を使用します。
レコードをメモリに読み取った後、ヌル文字 が追加されて適切な Cストリング .忘れないでください。そうしないと、クラッシュします。前と同様に、両方のファイルで fclose が呼び出されます。 fclose を忘れても (書き込みとは異なり) データが失われることはありませんが、メモリ リークが発生します。