C言語編 逆引き ファイルをコピーする

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

目的

ファイルをコピーしたいとします。

方法@(1Byteずつコピーする処理を自作する)[C89〜]

残念ながら、C言語の標準ライブラリには、ファイルをコピーする関数はありません。 そこで、コピー元ファイルとコピー先ファイルをそれぞれ開き、 1Byte ずつ読み込み、書き込むという動作を繰り返すように実装します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
	ファイルをコピーする。

	srcPath:  コピー元ファイルのパス
	destPath: コピー先ファイルのパス
	戻り値:   成功したら 0以外、失敗したら 0
*/
static int copyFile(const char* srcPath, const char* destPath)
{
	FILE* fpSrc;
	FILE* fpDest;
	int result = 1;

	/* コピー元とコピー先が同じ */
	if (strcmp(srcPath, destPath) == 0) {
		return 0;
	}

	/* バイナリモードで開く */
	fpSrc = fopen(srcPath, "rb");
	fpDest = fopen(destPath, "wb");
	if (fpSrc == NULL || fpDest == NULL) {
		result = 0;
	}

	if (result != 0) {
		for (;;) {
			char c;

			/* 1Byte 読み込む */
			fread(&c, sizeof(c), 1, fpSrc);
			if (feof(fpSrc)) {
				break;
			}
			if (ferror(fpSrc)) {
				result = 0;
				break;
			}

			/* 1Byte 書き込む */
			fwrite(&c, sizeof(c), 1, fpDest);
			if (ferror(fpDest)) {
				result = 0;
				break;
			}
		}
	}

	if (fpDest != NULL) {
		if (fclose(fpDest) == EOF) {
			result = 0;
		}
	}
	if (fpSrc != NULL) {
		if (fclose(fpSrc) == EOF) {
			result = 0;
		}
	}

	return result;
}

int main(void)
{
	if (copyFile("test.txt", "test_copy.txt")) {
		puts("コピーしました。");
	}
	else {
		puts("コピーに失敗しました。");
	}

	return 0;
}

実行結果:

コピーしました。

1行ずつ読み込むような実装だと、十分なサイズのバッファが必要になりますが、 どんなファイルか分からない場合、「十分なサイズ」を決定することができません。 そのため、1Byte ずつ読み込む必要があります。

また、Windows では、fopen関数の第2引数に "b" を加えてバイナリモードにしないと、 バイナリファイルのコピーの際に、改行文字と一致する値が含まれていたときに、 改行文字としての扱いを受けて、変換されてしまいます。

なお、この実装では、コピー先のファイルが既に存在していると、上書きされることに注意して下さい。
ファイルが既に存在していた場合にコピーを行わないようにすることは可能ですが、 その場合、同じプログラムを繰り返し実行すると、2度目以降、コピーが行えなくなります。 ファイルの自動バックアップのような用途では、これは好ましい動作では無いでしょう。
一方で、偶然、コピー後のファイルと同じ名前の無関係なファイルが無いとも限りません。 この場合は、上書きされてしまう方が好ましくない動作かも知れません。

この実装は、C言語の標準機能だけで実装できますが、 各環境が用意している機能を使った方が、 その環境特有の事情を考慮した実装がなされているであろうことから、 より確実かも知れません。

方法A(CopyFile関数を使う)[Windows]

Windows API には、ファイルのコピーを行う CopyFile関数(⇒MSDN)が用意されています。 また、コピー途中でキャンセルする等、より高度な機能を備えて CopyFileEx関数(⇒MSDN)があります。 ここでは CopyFile関数を取り上げます。

#include <stdio.h>
#include <Windows.h>

/*
	ファイルをコピーする。

	srcPath:  コピー元ファイルのパス
	destPath: コピー先ファイルのパス
	戻り値:   成功したら 0以外、失敗したら 0
*/
static int copyFile(const char* srcPath, const char* destPath)
{
	return CopyFileA(srcPath, destPath, FALSE);
}

int main(void)
{
	if (copyFile("test.txt", "test_copy.txt")) {
		puts("コピーしました。");
	}
	else {
		puts("コピーに失敗しました。");
	}

	return 0;
}

実行結果:

コピーしました。

CopyFile関数を使用するには、Windows.h のインクルードが必要です。 また、CopyFile は実際にはマクロになっていて、UNICODEマクロの定義の有無によって、 ANSI版(char型)の CopyFileA と、Unicode版(wchar_t型)の CopyFileW のいずれかに置換されます。 上のサンプルプログラムでは、文字列を const char* で扱っているため、 CopyFileA の方を直接呼び出すようにしています。

CopyFile関数の第3引数は、コピー先のファイルが既に存在していた場合に、 エラーとみなすか、上書きするかを選択するものです。 TRUE を指定するとエラーになり、FALSE を指定すると上書きされます。


参考リンク

更新履歴

'2017/6/17 新規作成。



逆引きのトップページへ

C言語編のトップページへ

Programming Place Plus のトップページへ