
C言語入門|ランダムアクセスの基本
これまで、ファイルを先頭から順番に読み書きする方法を学んできました。
しかし実際のプログラムでは、
- ファイルの途中にあるデータだけを読みたい。
- 決まった位置にあるレコードを直接更新したい。
といった場面もよくあります。
このように、ファイルの任意の位置に直接アクセスする方法を
ランダムアクセス(random access)といいます。
これに対して、先頭から順に読み書きする方法は
シーケンシャルアクセス(sequential access)と呼ばれます。

シーケンシャルアクセスとランダムアクセスの違い
まずは2つの違いを整理しておきましょう。
| アクセス方法 | 特徴 |
|---|---|
| シーケンシャルアクセス | 先頭から順番に読み書きする。 |
| ランダムアクセス | ファイルの任意の位置に直接移動して読み書きする。 |
ランダムアクセスは自由度が高く便利ですが、
必ずしも万能ではないという点が重要です。
なぜ基本はシーケンシャルアクセスなのか
ランダムアクセスが便利なのに、
なぜ「基本はシーケンシャル」と言われるのでしょうか。
理由の1つは、記憶装置の仕組みにあります。
従来よく使われてきたハードディスクドライブ(HDD)は、
- 円盤(磁気ディスク)が回転し
- 読み取りヘッド(針)が移動してデータを探す
という構造になっています。
読み書きしたい場所へ針を動かすことを シーク(seek)
その移動にかかる時間を シークタイム(seek time) といいます。
ランダムアクセスを多用すると、
- 針の移動が頻繁になる。
- シークタイムが増える。
- 全体の処理速度が低下する。
という問題が起こりやすくなります。
最近主流の SSD は物理的な針を持たないため影響は小さいですが、
それでも 不要なランダムアクセスは避けるのが基本です。
また、バックアップ用のテープ装置などでは
そもそもランダムアクセスができない、または非常に遅い場合もあります。
C言語におけるランダムアクセスの考え方
C言語では、ランダムアクセスを行うために
- 読み書き位置を移動する。
- 現在の位置を取得する。
という仕組みが用意されています。
ただし、テキストモードでのランダムアクセスは注意点が多いため、
本記事では バイナリモードでの利用を前提として解説します。
fseek ― 読み書き位置を移動する
ファイルの読み書き位置を変更するには、fseek 関数を使います。
書式
int fseek(FILE* fp, long offset, int origin);引数の意味
| 引数 | 説明 |
|---|---|
| fp | ファイルポインタ |
| offset | 基準位置から移動するバイト数 |
| origin | 基準位置 |
基準位置には、次の定数を指定します。
| 定数 | 基準となる位置 |
|---|---|
| SEEK_SET | ファイルの先頭 |
| SEEK_CUR | 現在の位置 |
| SEEK_END | ファイルの終端 |
使用例
fseek(fp, 9L, SEEK_SET);この場合、
ファイルの先頭から9バイト目に読み書き位置が移動します。
ftell ― 現在の読み書き位置を取得する
現在の読み書き位置を知りたい場合は、ftell 関数を使います。
書式
long ftell(FILE* fp);戻り値
- ファイル先頭からのバイト数
- 失敗時は -1L
使用例
long pos = ftell(fp);これで、現在の読み書き位置を取得できます。
fseek と ftell を組み合わせた考え方
fseek と ftell は、組み合わせて使うことで柔軟な制御ができます。
たとえば、
fseek(fp, ftell(fp) - 16L, SEEK_SET);このコードは、
- 現在の位置を取得
- そこから16バイト前に戻る
という意味になります。
サンプルプログラム:指定位置の int 型データを読み取る
次のプログラムは、
- int 型データを3個バイナリで書き込み
- 2番目のデータだけをランダムアクセスで読み取る
例です。
プロジェクト名:14-12-1 ソースファイル名: sample14-12-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE* fp;
int data[3] = { 10, 20, 30 };
int value;
// 書き込み
if ((fp = fopen("data.dat", "wb")) == NULL) {
exit(1);
}
fwrite(data, sizeof(int), 3, fp);
fclose(fp);
// 読み取り
if ((fp = fopen("data.dat", "rb")) == NULL) {
exit(1);
}
// 2番目のint型へ移動
fseek(fp, sizeof(int), SEEK_SET);
fread(&value, sizeof(int), 1, fp);
fclose(fp);
printf("読み取った値:%d\n", value);
return 0;
}実行結果
読み取った値:20このように、
固定サイズのデータを扱う場合、ランダムアクセスは非常に相性が良い
ということがわかります。
まとめ
- ランダムアクセスはファイルの任意位置に直接アクセスできる。
- 基本はシーケンシャルアクセスだが、必要な場面では非常に便利
- fseek で位置を移動し、ftell で現在位置を取得する。
- バイナリモードでの利用が安全でおすすめ。
