ファイル入出力(2)

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

前回はread(2)を使用したファイルの読み込み方を解説しました。今回はファイル操作の残りの基本機能である、write(2)の使い方を解説していきます。
また、いままでになかったエラーの種類についても解説をします。エラーにはいくつか原因があり、エラーによって対処方法も異なります。

ファイルの書き込み

write(2)のプロトタイプ

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

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

動作としては、countは書き込み元データのbufのサイズを指定します。戻り値は書き込んだファイルサイズを返します。エラーの場合は負数となります。

また、注意事項としてエラーにならなくても指定したサイズすべてが書き込まない場合があります。戻り値とcountとのチェックは必ず必要です。
これの場合の理由はいくつか考えられます。たとえば、書き込み先に空き容量が無い場合、OS側の都合で処理が途中で処理が止まった場合等、色々なパターンがあります。


サンプルコード

では早速write(2)を使ってたプログラムを紹介します。
処理内容はファイルに固定長データを書き込みをするだけです。
//write_file.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(void)
{
	int fd;
	int old_errno;
	char buf[512] = {};
	ssize_t ret;

	fd = open("test.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
	if (fd < 0) {
		fprintf(stderr, "open(2) errror:%d\n", fd);
		return 1;
	}

	ret = write(fd, buf, sizeof(buf));
	if (ret < 0) {
		old_errno = errno;
		if (old_errno == ENOSPC) { //空きスペースが無い
			fprintf(stderr, "Error:No left on space.\n");
		}
		return 1;
	}

	close(fd);

	printf("buffer size=%ld, write size=%ld\n", sizeof(buf), ret);

	return 0;
}

解説は後回しにしてまずは実行してみます。実行すると書き込んだファイルサイズが正しく表示されているのが分かります。
$ gcc -o write_file write_file.c
$ ./write_file
buffer size=512, write size=512

サンプルコード解説

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

まずは普通にファイルオープンします。
注意する部分は、ファイル権限の設定です。ファイルのオープン、クローズを思い出してください。
	fd = open("test.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
	if (fd < 0) {
		fprintf(stderr, "open(2) errror:%d\n", fd);
		return 1;
	}

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

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

次にエラーチェックを行います。
エラーには種類があると言いましたが、この種類を判断するのが「errno」というグローバル変数です。これを利用するには"errno.h"をincludeする必要があります。
errnoに何が入るか、入っているエラーの意味は各manにある程度書いています。ある程度という事はすべて書いていないと言うことです。これには理由がありますが、初めの内は気にしなくてもよいでしょう。まずはmanをしっかり使いこなせるようになることです。
errnoの注意事項としてシステム関数がエラーになった場合に自動的に書き換えます。よって、エラーになればすぐに、errnoのコピーを取っておく必要があります。そうしないと思わぬ所でerrnoが書き換えられてエラーの原因が分からなくなります。
今回は、ENOSPCをチェックしています。ENOSPCはスペースが無い(no left on space)場合に発生するエラーです。今回はwrite(2)を使用しているので書き込み先のディスクがいっぱいという意味です。
	if (ret < 0) {
		old_errno = errno;
		if (old_errno == ENOSPC) { //空きスペースが無い
			fprintf(stderr, "Error:No left on space.\n");
		}
		return 1;
	}

あとはファイルのクローズと結果の出力をしてプログラム終了です。
	close(fd);

	printf("buffer size=%ld, write size=%ld\n", sizeof(buf), ret);

	return 0;

いかがでしたか? 今回の肝はerrnoです。今回チェックしたのはENOSPCだけでしたが他に使用する可能性があるエラーの種類をあげておきます。

  • EBADF: fdが有効ではない
  • EFAULT: bufが異常なアドレスをさしている
  • EFBIG: countが大きすぎる

EBADF、EFAULTが発生したら明らかなバグでしょう。しかし、EFBIGは大きすぎるデータを書き込む場合には発生しえます。このサイズがどの程度適切かはOSによって違います。このエラーがでるようであれば、bufを分割して少しずつwrite(2)する必要があるでしょう。

errnoはシステムの状態やバグを知るための1つの方法でもあります。ファイル入出力(1)では説明を省略しましたが、これを期にread(2)等他のシステムコールでもきちんとチェックするくせを付けてください。
ちなみに、エラー処理は非常に多くのコードになるため、本サイトではできるだけ使わない、もしくは、可能な限りコードの量を抑えることとします。しかし、自分でプログラムを書く場合には忘れないようにしてください。


まとめ

これで、ファイル処理の基本の解説が終了しました。
次の解説では、ファイルのランダムアクセスの解説をします