ファイル入出力(1)

このエントリーをはてなブックマークに追加

前回はファイル操作に必要なopen(2)、close(2)の使い方を解説しました。今回はさらにread(2)の使い方を解説していきます。
ここでは通常のファイルを対象として、ファイルの読み方の基本を解説します。単純な操作ですが非常に重要です。ここでしっかり使い方をマスターしましょう。

ファイルの読み込み

read(2)のプロトタイプ

read(2)のプロトタイプです。
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

動作としては、countは読み込み先のbufのサイズ、つまり最大読み込みサイズを指定します。戻り値は読み込んだファイルサイズを返します。エラーの場合は負数となります。つまり、戻り値が0ならばファイルの終端と判断して構いません。

また、注意事項としてシステム関数はバッファ機能が無いため自前でバッファを容易する必要があります。例えば、ファイルから1バイトずつ読み込み処理を行いたい場合でも、先に一定のデータを読み込み、そのバッファを処理するという形を取ります。
これはシステムコールを1度呼ぶと、一旦OSに処理が切り替わるためです。1バイトの読み込み事にOSに処理が渡るのは非常に効率が悪くなります。ですので、自前でバッファを用意する必要があるのです。


サンプルコード

では早速read(2)を使ってたプログラムを紹介します。
処理内容はファイルを読み込んで、ファイルサイズを表示するだけです。
//read_file.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	int fd;
	char buffer[512];
	ssize_t ret;
	ssize_t read_size=0;

	fd = open("sample", O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "open() errror:%d\n", fd);
		return 1;
	}

	while (1) {
		ret = read(fd, buffer, sizeof(buffer));
		if (ret < 0) {
			fprintf(stderr, "read() errror:%d\n", fd);
			close(fd);
			return 1;
		}

		if (ret == 0) {
			break;
		}
		read_size += ret;
	}

	close(fd);

	printf("read size=%ld\n", read_size);

	return 0;
}

解説は後回しにしてまずは実行してみます。実行すると読み込んだファイルサイズが正しく表示されているのが分かります。
ちなみに、1行目のddコマンドは読み込む為のファイルを作成しているだけです。ddコマンドについては説明をしません。詳しく知りたい方は各自で調べてください。
$ dd if=/dev/zero of=sample ibs=513 count=2
$ ls -l sample
-rw-r--r-- 1 khondalit khondalit 1026 2011-11-09 22:52 sample
$ gcc -o read_file read_file.c
$ ./read_file
read size=1026

サンプルコード解説

プログラムが正しく動いているのが確認出来たので、プログラムの解説をしていきます。

まずはファイルオープンですがこれは前回に説明したので特筆すべき事はないでしょう。
	fd = open("sample", O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "open() errror:%d\n", fd);
		return 1;
	}

次にwhileループ内部の処理になります。

まず、読み込み処理を行います。
		ret = read(fd, buffer, sizeof(buffer));

次にエラーチェックを行います。
エラーの場合close(2)を行います。通常read(2)がエラーの場合原因が何であれそれ以上処理することは出来ません。ファイルの場合ファイルのEOFに達したか、何らかの障害が発生していますのでこれ以上処理を続けないようにreternします。状況にもよりますが出来ればプロセス自体を終了するのが良いでしょう。今回はmain関数での処理ですのでretrunでプロセスが終了します。
		ret = read(fd, buffer, sizeof(buffer));
		if (ret < 0) {
			fprintf(stderr, "read() errror:%d\n", fd);
			close(fd);
			return 1;
		}

次にファイルの終了判断です。read(2)の戻り値が0の場合はファイルの終了を意味していますのでbreakして読み込み処理を終了します。
		if (ret == 0) {
			break;
		}

つぎにファイルサイズの計算です。読み込んだサイズ分を足していきます。
	read_size += ret;

これでループ内の処理が終了となります。ここまでが読み込み処理となります。

ファイルの処理は終了したので、すぐにファイルをクローズします。
そして最後にファイルサイズを出力してプログラムが終了となります。
	close(fd);

	printf("read size=%ld\n", read_size);

	return 0;

まとめ

いかがだったでしょうか? 非常に簡単な処理ですが、この読み込み処理は基本ですがエラー処理など非常に大切な部分も含まれています。
また、この処理はパイプをはじめとしたプロセス間通信やネットワーク処理にも通じるUnixでの基本となる部分です。
ここで処理を良く理解しておくようにしましょう。


次の解説では、read(2)と対をなすwrite(2)の解説を行います。これさえ理解すればファイル処理の基本部分が終了します。