ディレクトリの操作

ファイル処理9

ファイルに続き、ディレクトリ(フォルダ)に対する操作をまとめます。
なお、ディレクトリ名の変更はその他のファイル操作を参照してください。

ファイルというのはOSや他の様々なプログラムからアクセスされる可能性があります。
他のプログラムが操作中のファイルに対する処理はエラーが発生する可能性があります。
実際のプログラムではエラー発生時の処理を適切に行う必要があります。

このページで説明する関数はすべてC言語標準関数ではありません。
コンパイラによっては使用できないものもあります。

ディレクトリ(フォルダ)作成

ディレクトリの作成にはmkdir関数(_mkdir関数)を使用します。
_mkdir関数はVisual Studioでのみ使用可能です。

mkdir関数

mkdir関数の使用には#include <sys/stat.h>が必要です。


#include <stdio.h>
#include <sys/stat.h>

int main()
{
    const char *dir = "./test";

    if (mkdir(dir, 0777) == 0)
        printf("ディレクトリ%sを作成しました。\n", dir);
    else
        printf("ディレクトリ%sを作成できませんでした。\n", dir);

    getchar();
}
int mkdir(
 const char *dirname
 mode_t permission
);
ディレクトリdirnameをパーミッションpermissionの設定で作成する。
成功した場合は0を、失敗した場合は0以外を返す。

上記コードは相対パスで、実行ファイルと同じ場所に「test」という名前のディレクトリを作成しています。
すでに同名のディレクトリが存在する場合は作成に失敗します。

プログラムをVisual Studio上から実行している場合はプロジェクトファイルのあるフォルダが相対パスの起点となります。
(実行ファイルやソリュージョンファイルのあるフォルダではない)

第二引数はパーミッションの設定を8進数で指定しています。

パーミッションというのはUNIX系OSで採用されている、ファイルやディレクトリに対するアクセス権限の設定です。

第二引数の数値の意味は、まず先頭が8進数リテラルを表す「0」です。
続いてOwner権限、Group権限、Other権限の順で指定で、「4」で読み込み、「2」で書き込み、「1」で実行の権限を表します。
(「0」は権限なし)
読み込みと実行の許可を与える場合は「4+1」で「5」を指定します。
(コンパイラによってはこれらの定数も用意されています。)
ただしumask値の影響を受けます。

Windowsユーザーにはなじみのないものかと思いますが、Windows上での実行においては(たぶん)意味のない指定です。

mkdir関数は、ディレクトリの階層構造を一気に作成することはできません。
階層構造の上位ディレクトリが存在しない場合は実行に失敗します。


#include <stdio.h>
#include <sys/stat.h>

int main()
{
    const char *dir = "./test/abc";

    //「test」というディレクトリが存在しない場合
	//「abc」フォルダは作成できず
    //mkdir関数は失敗する
    if (mkdir(dir, 0777) == 0)
        printf("ディレクトリ%sを作成しました。\n", dir);
    else
        printf("ディレクトリ%sを作成できませんでした。\n", dir);

    getchar();
}

_mkdir関数

_mkdir関数を使用するには#include <direct.h>が必要です。
direct.hはVisual Studioでのみ使用できるヘッダなので、以下のコードはVisual Studio以外では動作しません。


#include <stdio.h>
#include <direct.h>

int main()
{
    const char *dir = "./test";

    if (_mkdir(dir) == 0)
        printf("ディレクトリ%sを作成しました。\n", dir);
    else
        printf("ディレクトリ%sを作成できませんでした。\n", dir);

    getchar();
}
int _mkdir(
 const char *dirname
);
ディレクトリdirnameを作成する。
成功した場合は0を、失敗した場合は0以外を返す。

使い方はmkdir関数とほぼ同じで、第二引数のパーミッションの指定がないだけです。

errno

mkdir関数は処理に失敗した場合にerrnoを書き換えます。
より正確にエラー処理をする場合はこれを利用します。

errnoはC言語の関数が処理に失敗した場合に、失敗した理由が格納される特殊なグローバル値です。
(処理が失敗してもerrnoを書き換えない関数も多くあります)
ただしこの関数自体がC言語標準ではないため、設定される値も各コンパイラの実装によります。
以下の二つは大体共通しているようです。

EEXIST
指定のディレクトリが既に存在する
ENOENT
指定のパスが存在しない

errnoを使用するために<errno.h>をインクルードします。


#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>

int main()
{
    const char* dir = "./test/abc";

    //errnoを0に戻しておく
    errno = 0;
    if (mkdir(dir, 0777) == 0) {
        printf("ディレクトリ%sを作成しました。\n", dir);
    }
    else {
        if(errno == EEXIST)
            printf("ディレクトリ%sは既に存在します。\n", dir);
        else if(errno == ENOENT)
            printf("パス%sが存在しません。\n", dir);
        else
            printf("ディレクトリ%sを作成できませんでした。\n", dir);

        //errnoを0に戻しておく
        errno = 0;
    }

    getchar();
}

errnoはプログラム起動直後は0がセットされているので今回のコードではあらかじめ0で初期化する必要はありません。

errnoは、関数が成功した場合にその値がどうなるかは規定されていません。
関数が成功したら0にセットされるわけではありませんし、関数が成功してもerrnoの値が0以外にセットされることはルール上あり得ます。
そのため、mkdir関数のように関数の成否が戻り値で受け取れる場合は、まず戻り値でエラーの有無をチェックし、エラー値が返された場合にerrnoのでエラーの詳細をチェックします。

関数が正常な処理を行った場合でもerrnoは書き換わる可能性があるため、ある関数のエラーチェックをする場合はその関数を実行する直前でerrnoを0に戻しておき、実行の直後でerrnoの値を調べる必要があります。
それをしない場合のerrnoの値は不明なので、使用できません。

ディレクトリの削除

ディレクトリを削除するにはrmdir関数(_rmdir関数)を使用します。
rmdir関数は「#include <unistd.h>」が必要です。
_rmdir関数は「#include <direct.h>」が必要です。
(Visual Studio専用)


#include <stdio.h>
#include <unistd.h>

int main()
{
    const char *dir = "./test";

    if(rmdir(dir) == 0)
        printf("ディレクトリ%sを削除しました。\n", dir);
    else
        printf("ディレクトリ%sを削除できません。\n", dir);

    getchar();
}
int rmdir(
 const char *dirname
);
ディレクトリdirnameを削除する。
成功した場合は0を、失敗した場合は0以外を返す。

rmdir関数も_rmdir関数も使い方は同じです。

ディレクトリを削除するには、そのディレクトリが空である必要があります。
ディレクトリ内にファイルやフォルダが残っている場合は削除に失敗します。

ディレクトリの存在確認

ディレクトリの存在確認にはstat関数を使用します。
(stat関数に関してはファイルサイズの取得を参照)


#include <stdio.h>
#include <sys/stat.h>

int main()
{
    const char *dir = "C:\\ABC";
    struct stat statBuf;

    if (stat(dir, &statBuf) == 0)
        printf("ディレクトリ%sは存在します。\n", dir);
    else
        printf("ディレクトリ%sは存在しません。\n", dir);

    getchar();
}

stat関数にはディレクトリも指定できます。
stat関数が成功すればディレクトリが存在するということになります。

direct.hの他の関数

Visual Studioで使用できるヘッダdirect.hには他にもいくつかのディレクトリ操作用の関数があります。

_getcwd関数

_getcwd関数は、現在のワーキングディレクトリのパスを取得します。
_getdriveは、現在のワーキングディレクトリのあるドライブを取得します。


#include <stdio.h>
#include <direct.h>

#define MAX_PATH 260

int main()
{
    char buf[MAX_PATH];
    printf("%s\n", _getcwd(buf, MAX_PATH));
    printf("%d\n", _getdrive());

    getchar();
}
C\:Visual Studio Project\test\Debug
3
char* _getcwd(
 char *buffer
 int bufferSize
);
文字列bufferに現在のワーキングディレクトリのパスを格納する。
戻り値はbuffer。
取得されるパス文字列のサイズがbufferSizeを超えた場合はNULLを返す。
int _getdrive();
現在のワーキングディレクトリのあるドライブを1~26の整数で返す。
数字はA~Zのドライブ文字に対応する。

ワーキングディレクトリとは実行ファイルが現在使用しているディレクトリ(フォルダ)のことです。
(カレントディレクトリ、作業フォルダとも言う)
ファイルの読み取りや書き込みなどで相対パスを指定した場合に、パスの起点として使用されるディレクトリです。
規定では実行ファイルのある場所がワーキングディレクトリとなります。

プログラムをVisual Studio上から実行している場合はプロジェクトファイルのあるフォルダが相対パスの起点となります。
(実行ファイルやソリュージョンファイルのあるフォルダではない)

_chdir関数、_chdrive関数

_chdir関数は、現在のワーキングディレクトリを変更します。
_chdrive関数は、現在のドライブを変更します。

int _chdir(
 const char *dirname
);
現在のワーキングディレクトリをdirnameに変更する。
成功した場合は0を、失敗した場合は-1を返す。
int _chdrive(
 int drive
);
現在のワーキングディレクトリのあるドライブをdriveに変更する。
driveは1~26の整数で、A~Zのドライブ文字に対応する。
成功した場合は0を、失敗した場合は-1を返す。

#include <stdio.h>
#include <direct.h>

#define MAX_PATH 260

int main()
{
    char buf[MAX_PATH];

    //ワーキングディレクトリを
    //「C:/test」に変更
    //指定のディレクトリが無ければ失敗
    _chdir("C:/test");
    printf("%s\n", _getcwd(buf, MAX_PATH));

    //ワーキングドライブを
    //Eドライブに変更
    //Eドライブがなければ失敗
    _chdrive(5);
    printf("%s\n", _getcwd(buf, MAX_PATH));

    getchar();
}
C:\test
E:\

Windows環境に限定する場合、C言語関数よりもWindows APIを使用したほうがより高度な操作が可能です。
(→Windows API)