System.IO名前空間に用意されているクラスを使うことでファイルやディレクトリ(フォルダ)などファイルシステムに対する操作を行うことができます。 例えば、ファイルのコピー・削除・移動などの操作はFileクラス、ディレクトリの作成・削除・移動などの操作はDirectoryクラスのメソッドを使います。 また、FileInfo・DirectoryInfoのインスタンスを作成・取得することでファイルやディレクトリのプロパティを取得することもできます。

ここでは、System.IO名前空間にあるクラスのうち、ファイルやディレクトリのコピー・削除・移動など、ファイルシステムの操作に関するクラスの使い方について解説します。 ファイルに対する読み込み・書き込みについてはファイル入出力、あるいはFileStreamクラスStreamReaderクラス・StreamWriterクラスにて個別に解説していますのでそちらをご覧ください。

なお、ここではフォルダという用語は使わずクラス名にも使われているディレクトリで統一します。

本文中のサンプルではパスを記述するために逐語的文字列リテラルを使用している個所があります。

§1 ディレクトリの走査・ファイルの検索

ディレクトリを走査してディレクトリ内に含まれるファイルやサブディレクトリを取得するには、Directoryクラスに用意されているメソッドを使います。 単純な走査だけでなく、ワイルドカードによるパターンマッチングを使った検索や、ディレクトリ内の再帰的な走査も行うことができます。

ディレクトリ内にあるすべてのファイルを取得するにはGetFilesメソッド、サブディレクトリを取得するにはGetDirectoriesメソッドを使います。 これらのメソッドでは、引数として取得の対象となるディレクトリを相対パスまたは絶対パスで指定します。 存在しないディレクトリを指定した場合は例外DirectoryNotFoundExceptionがスローされます。

ディレクトリ内の全ファイル・サブディレクトリの取得
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ディレクトリE:\dirにあるすべてのファイルを取得する
    string[] files = Directory.GetFiles(@"E:\dir");

    foreach (string file in files) {
      Console.WriteLine(file);
    }

    // ディレクトリE:\dirにあるすべてのサブディレクトリを取得する
    string[] dirs = Directory.GetDirectories(@"E:\dir");

    foreach (string dir in dirs) {
      Console.WriteLine(dir);
    }
  }
}
実行結果例
E:\dir\file1.txt
E:\dir\file2.bmp
E:\dir\file3.dat
E:\dir\subdir1
E:\dir\subdir2

GetFiles・GetDirectoriesメソッドで特にオプションを指定しない場合、取得されるのは指定したディレクトリ直下にあるファイル・サブディレクトリのみとなります。 この動作を変更するには後述するSearchOptionを指定する必要があります。

GetFileSystemEntriesメソッドを使うと指定ディレクトリにあるファイルとサブディレクトリの両方をまとめて取得することができます。 このメソッドはGetFilesとGetDirectoriesの結果を組み合わせたものと同等となります。

GetFileSystemEntriesを使ってディレクトリ内の全ファイル・サブディレクトリの取得する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ディレクトリE:\dirにあるすべてのファイルとサブディレクトリを取得する
    string[] entries = Directory.GetFileSystemEntries(@"E:\dir");

    foreach (string entry in entries) {
      Console.WriteLine(entry);
    }
  }
}
実行結果例
E:\dir\file1.txt
E:\dir\file2.bmp
E:\dir\file3.dat
E:\dir\subdir1
E:\dir\subdir2

§1.1 ワイルドカードを指定した検索

GetFiles・GetDirectories・GetFileSystemEntriesの各メソッドは引数にワイルドカードを指定することにより、ワイルドカードにマッチするファイル・サブディレクトリだけを取得するようにすることもできます。 引数にワイルドカードを指定しない場合はすべてのファイル・サブディレクトリが取得されます。 *を指定した場合も同様にすべてのファイル・サブディレクトリが取得されます。

ワイルドカードを用いたファイル・サブディレクトリの取得
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // カレントディレクトリにあるすべてのファイルを取得する
    Console.WriteLine("[*]");

    foreach (string file in Directory.GetFiles(".", "*")) {
      Console.WriteLine(file);
    }

    // カレントディレクトリにあるすべての.txtファイルを取得する
    Console.WriteLine("[*.txt]");

    foreach (string file in Directory.GetFiles(".", "*.txt")) {
      Console.WriteLine(file);
    }
  }
}
実行結果
[*]
.\file1.txt
.\file2.bmp
.\file3.dat
[*.txt]
.\file1.txt

ワイルドカードには任意の1文字を表す?および任意の0文字以上を表す*を含めることができます。

GetFilesメソッド・GetDirectoriesメソッドでは正規表現を使ったマッチングはできないので、必要な場合はワイルドカードを指定する代わりにRegexクラスを使って独自にコードを記述する必要があります。

§1.2 ディレクトリ走査のオプション (SearchOption)

引数にSearchOptionを指定することにより、サブディレクトリを再帰的に走査するかどうかを指定することができます。 SearchOption.TopDirectoryOnlyを指定した場合は対象のディレクトリ直下のみを走査し、SearchOption.AllDirectoriesを指定した場合は対象のディレクトリ内とそのサブディレクトリすべてを再帰的に走査します。

SearchOptionを指定したディレクトリ内の走査
using System;
using System.IO;

class Sample {
  static void Main()
  {
    string[] files;

    // カレントディレクトリ直下にあるすべてのファイルを取得する
    Console.WriteLine("[TopDirectoryOnly]");

    files = Directory.GetFiles(".", "*", SearchOption.TopDirectoryOnly);

    foreach (string file in files) {
      Console.WriteLine(file);
    }

    // カレントディレクトリとそのサブディレクトリ内にあるすべてのファイルを取得する
    Console.WriteLine("[AllDirectories]");

    files = Directory.GetFiles(".", "*", SearchOption.AllDirectories);

    foreach (string file in files) {
      Console.WriteLine(file);
    }
  }
}
実行結果
[TopDirectoryOnly]
.\file1.txt
.\file2.bmp
.\file3.dat
[AllDirectories]
.\file1.txt
.\file2.bmp
.\file3.dat
.\subdir1\bar.txt
.\subdir1\baz.txt
.\subdir1\foo.txt
.\subdir2\hoge.dat

§1.3 列挙子によるディレクトリの走査

ファイル・ディレクトリの走査するメソッドにはGetFiles/GetDirectories/GetFileSystemEntriesだけでなくEnumerateFiles/EnumerateDirectories/EnumerateFileSystemEntriesというメソッドも用意されています。 これは.NET Framework 4で追加されたメソッドで、どちらも基本的な使い方や得られる結果は同じですが、動作が異なります。

GetFilesメソッドは見つかったファイル・ディレクトリを配列で返すのに対して、EnumerateFilesメソッドはIEnumerable<string>で返します。 GetFilesメソッドではすべてのファイル・ディレクトリの走査が終わるまで結果が返されないのに対して、EnumerateFilesメソッドではメソッドの呼び出し自体は即座に終了し、走査はIEnumerable<string>を列挙する際に開始され、見つかったファイルが順次返されます。 従って、条件に一致するもののうち最初に見つかったものだけを取得したい場合などにはEnumerateFilesメソッドを使った方が効率的です。

EnumerateFilesを使ったディレクトリの走査
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // 最初に見つかった.txtファイルを表示する
    foreach (string file in Directory.GetFiles(".", "*.txt")) {
      Console.WriteLine(file);
      break; // 列挙を中断する
    }

    // 最初に見つかった.txtファイルを表示する
    foreach (string file in Directory.EnumerateFiles(".", "*.txt")) {
      Console.WriteLine(file);
      break; // 列挙を中断する
    }
  }
}

上記のコードでは実行結果はどちらも同じとなりますが、仮にディレクトリ内に多数のファイルがある場合、GetFilesメソッドではそのすべてを含む配列が返されるまで列挙が始まらないのに対し、EnumerateFilesでは見つかったファイルから順次列挙されていくことになります。 従って、一般的にGetFilesよりもEnumerateFilesを使った方がよいパフォーマンスが得られます。

一方、EnumerateFilesメソッドから返されるIEnumerable<string>は、列挙を行う度にディレクトリが再走査されます。 従って、EnumerateFilesメソッドで得られる結果を何度も列挙するような場合には、EnumerateFilesよりもGetFilesメソッドを使ったほうがよいパフォーマンスが得られます。

GetFiles/EnumerateFilesだけでなく、GetDirectories/EnumerateDirectoriesおよびGetFileSystemEntries/EnumerateFileSystemEntriesでもこのような特性となります。

§1.4 走査結果の順序とソート

GetFiles・EnumerateFilesなどディレクトリの走査を行うメソッドは、どのような順序で結果が返されるかは規定されていません。 従って、ファイルやディレクトリの検索結果を名前順や更新日時順にしたいといった場合は、別途ソートを行う必要があります。

次の例では、GetFilesメソッドで取得したファイルをソートし、GetLastWriteTimeメソッドによって取得した最終書き込み日時の順となるようにして表示しています。 ソートにはArray.Sortメソッドを使っています。

GetFilesメソッドで取得したファイルのソート
using System;
using System.IO;

class Sample {
  // 二つのファイルの最終書き込み日時を取得して比較するメソッド
  static int CompareLastWriteTime(string fileX, string fileY)
  {
    return DateTime.Compare(File.GetLastWriteTime(fileX), File.GetLastWriteTime(fileY));
  }

  static void Main()
  {
    // カレントディレクトリ直下にあるすべてのファイルを取得する
    string[] files = Directory.GetFiles(".");

    // 取得したすべてのファイルを最終書き込み日時順でソートする)
    Array.Sort(files, CompareLastWriteTime);

    // ソートした結果を表示
    foreach (string file in files) {
      // ファイル名と最終書き込み日時を表示
      Console.WriteLine("{0} {1}", File.GetLastWriteTime(file), file);
    }
  }
}
実行結果例
05/16/2012 02:44:34 .\test.cs~
09/19/2013 21:07:11 .\log.txt
12/01/2013 22:33:58 .\log.xml
12/05/2013 22:09:56 .\sample.txt
12/08/2013 22:56:27 .\test.vb
12/08/2013 22:59:17 .\test.cs
12/08/2013 22:59:19 .\test.exe

次の例は先の例と同じくファイルを最終書き込み日時の順にして表示していますが、EnumerateFilesメソッドとOrderByメソッドを使うようにしています。

EnumerateFilesメソッドで取得したファイルのソート
using System;
using System.IO;
using System.Linq;

class Sample {
  static void Main()
  {
    // カレントディレクトリ直下にあるすべてのファイルを取得し、
    // 最終書き込み日時順に並べ替えて列挙する
    foreach (string file in Directory.EnumerateFiles(".").OrderBy(f => File.GetLastWriteTime(f))) {
      // ファイル名と最終書き込み日時を表示
      Console.WriteLine("{0} {1}", File.GetLastWriteTime(file), file);
    }
  }
}
実行結果例
05/16/2012 02:44:34 .\test.cs~
09/19/2013 21:07:11 .\log.txt
12/01/2013 22:33:58 .\log.xml
12/05/2013 22:09:56 .\sample.txt
12/08/2013 22:56:27 .\test.vb
12/08/2013 22:59:17 .\test.cs
12/08/2013 22:59:19 .\test.exe

ソートについて詳しくは基本型のソートと昇順・降順でのソートおよび複合型のソート・複数キーでのソートで解説しています。



§2 ディレクトリに対する操作 (Directoryクラス)

ディレクトリの作成・削除・移動などの操作を行うにはDirectoryクラスのメソッドを使います。 以下で解説するメソッドでは、操作対象となるディレクトリを引数で指定します。 ワイルドカードや正規表現によるパターンマッチングを使って複数のディレクトリをまとめて削除したり移動することはできないので、そういった場合にはDirectory.GetDirectoriesメソッドと組み合わせて使う必要があります。

§2.1 存在するか調べる (Directory.Exists)

ディレクトリが存在するかどうかを確かめるにはExistsメソッドを使用します。

ディレクトリが存在するかどうか調べる
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ディレクトリE:\dir\が存在するか調べる
    if (Directory.Exists(@"E:\dir"))
      Console.WriteLine("ディレクトリは存在します");

    // カレントディレクトリにディレクトリsubdirが存在するか調べる
    if (Directory.Exists("subdir"))
      Console.WriteLine("ディレクトリは存在します");
  }
}

§2.2 作成 (Directory.CreateDirectory)

ディレクトリを作成するにはCreateDirectoryメソッドを使用します(メソッド名はCreateではないので注意)。 このメソッドでは引数で指定した名前のディレクトリが作成されますが、指定したディレクトリ名が階層をなす場合(dir\subdir1\subdir2などサブディレクトリを含む場合)はサブディレクトリも含めて一度に作成されます。 このメソッドは、作成されたディレクトリ(既に存在している場合はそのディレクトリ)のDirectoryInfoを返します。

ディレクトリを作成する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ディレクトリE:\dirを作成する
    Directory.CreateDirectory(@"E:\dir");

    // カレントディレクトリにsubdir1\subdir2を作成する
    Directory.CreateDirectory(@"subdir1\subdir2");
  }
}

CreateDirectoryメソッドはすでにディレクトリが存在する場合でも例外はスローされずメソッドは成功します。 そのため、事前にディレクトリが存在するかチェックし、存在しない場合のみCreateDirectoryメソッドで作成する、といったことをする必要はありません。

ディレクトリを作成する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // カレントディレクトリにsubdir1\subdir2が存在しない場合のみ作成する
    Directory.CreateDirectory(@"subdir1\subdir2");

    // ディレクトリが存在していてもCreateDirectoryは例外をスローしないので、
    // 以下のように作成前にDirectory.Existsで存在するかチェックする必要はない
    if (!Directory.Exists(@"subdir1\subdir2"))
      Directory.CreateDirectory(@"subdir1\subdir2");
  }
}

§2.3 削除 (Directory.Delete)

ディレクトリを削除するにはDeleteメソッドを使用します。 このメソッドは対象のディレクトリが空の場合のみディレクトリを削除できます。 対象のディレクトリにファイルやサブディレクトリが存在する場合にはIOExceptionをスローします。 ファイルやサブディレクトリを含めて再帰的に削除したい場合は、二番目の引数recursivetrueを指定します。 また、ディレクトリが別のプロセスによって使用されていて削除できない場合にもIOExceptionをスローするので注意が必要です。

ディレクトリを削除する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    try {
      // ディレクトリsubdirを削除する
      Directory.Delete("subdir");
    }
    catch (IOException) {
      // ディレクトリ内にファイルまたはサブディレクトリがある場合、
      // もしくは別のプロセスがディレクトリを使用中の場合はIOExceptionがスローされる
    }

    try {
      // ディレクトリsubdirとディレクトリ内のすべてのファイル・サブディレクトリを削除する
      Directory.Delete("subdir", true);
    }
    catch (IOException) {
      // 別のプロセスがディレクトリを使用中の場合はIOExceptionがスローされる
    }
  }
}

§2.4 移動・リネーム (Directory.Move)

ディレクトリを移動(リネーム)するにはMoveメソッドを使用します。

ディレクトリを移動する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // カレントディレクトリにあるsubdir1をtempに移動(リネーム)する
    Directory.Move("subdir1", "temp");

    // カレントディレクトリにあるsubdir2をtempディレクトリの下に移動する
    Directory.Move("subdir2", @"temp\subdir2");

    // E:\dirをF:\に移動する(異なるボリュームへの移動なのでIOExceptionをスローする)
    Directory.Move(@"E:\dir", @"F:\");
  }
}

このメソッドでは既に存在するディレクトリ名へのリネームや移動元の子ディレクトリとなるようなディレクトリに移動することはできません。 そういった場合は、ディレクトリ内のファイル・サブディレクトリを個別に移動する必要があります。 また、異なるドライブ(ボリューム)への移動もサポートされません。 この場合は移動先へのコピーを行ったのち、移動元を削除することによって移動する必要があります。

§2.5 コピー

Directoryクラスにはディレクトリごと別のディレクトリにコピーするメソッドは用意されていません。 そのため、GetFileSystemEntriesなどのメソッドによってディレクトリ内のファイル・サブディレクトリを再帰的に列挙し、1つづつコピーしていく必要があります。

以下のコードは、ディレクトリのコピーを行うメソッドの例です。

ディレクトリをコピーする
using System;
using System.IO;

class Sample {
  /// <summary>ディレクトリ内のファイルおよびサブディレクトリをコピーする</summary>
  /// <remarks>このメソッドはディレクトリの属性をコピーしない</remarks>
  /// <param name="sourceDirectory">コピー元のディレクトリを表す絶対パスまたは相対パス</param>
  /// <param name="destDirectory">コピー先のディレクトリを表す絶対パスまたは相対パス</param>
  /// <param name="overwrite">コピー先に同名のファイルが存在する場合に上書きを許可するかどうか</param>
  /// <exception cref="IOException"><paramref name="overwrite"/>がfalseの場合、かつコピー先に同名のファイルが存在する</exception>
  static void CopyDirectory(string sourceDirectory, string destDirectory, bool overwrite)
  {
    // 存在しない場合はコピー先ディレクトリを作成する
    Directory.CreateDirectory(destDirectory);

    // コピー元のディレクトリにあるファイルをコピーする
    foreach (var sourceFile in Directory.EnumerateFiles(sourceDirectory)) {
      var destFile = Path.Combine(destDirectory, Path.GetFileName(sourceFile));

      File.Copy(sourceFile, destFile, overwrite);
    }

    // 再帰呼び出しによりコピー元のディレクトリにあるサブディレクトリをコピーする
    foreach (var sourceSubDirectory in Directory.EnumerateDirectories(sourceDirectory)) {
      // サブディレクトリの名前は最後のディレクトリ区切り文字以降の部分となるので、
      // Path.GetDirectoryNameではなくPath.GetFileNameを使ってサブディレクトリ名を取得する
      var destSubDirectory = Path.Combine(destDirectory, Path.GetFileName(sourceSubDirectory));

      CopyDirectory(sourceSubDirectory, destSubDirectory, overwrite);
    }
  }

  static void Main()
  {
    // カレントディレクトリにあるdirディレクトリをコピーして_backupディレクトリを作成する
    CopyDirectory("dir", "_backup", false);
  }
}

§3 ファイルに対する操作 (Fileクラス)

ファイルのコピー・削除・移動などの操作を行うにはFileクラスのメソッドを使います。 以下で解説するメソッドでは、操作対象となるファイルを引数で指定します。 ワイルドカードや正規表現によるパターンマッチングを使って複数のファイルをまとめて削除したり移動することはできないので、そういった場合にはDirectory.GetFilesメソッドと組み合わせて使う必要があります。

§3.1 存在するか調べる (File.Exists)

ファイルが存在するかどうかを確かめるにはExistsメソッドを使用します。

ファイルが存在するかどうか調べる
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ファイルE:\dir\file.txtが存在するか調べる
    if (File.Exists(@"E:\dir\file.txt"))
      Console.WriteLine("ファイルは存在します");

    // カレントディレクトリにファイルfile.txtが存在するか調べる
    if (File.Exists("file.txt"))
      Console.WriteLine("ファイルは存在します");
  }
}

§3.2 削除 (File.Delete)

ファイルを削除するにはDeleteメソッドを使用します。 ファイルが別のプロセスによって使用されていて削除できない場合にはIOExceptionをスローするので注意が必要です。

ファイルを削除する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    try {
      // ファイルfile.txtを削除する
      File.Delete("file.txt");
    }
    catch (IOException) {
      // 別のプロセスがファイルを使用中の場合はIOExceptionがスローされる
    }
  }
}

§3.3 移動・リネーム (File.Move)

ファイルを移動(リネーム)するにはMoveメソッドを使用します。

ファイルを移動・リネームする
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // カレントディレクトリにあるfile1.txtをoldfile.txtに移動(リネーム)する
    File.Move("file1.txt", "oldfile.txt");

    // カレントディレクトリにあるfile2.txtをディレクトリE:\dir\に移動する
    File.Move("file2.txt", @"E:\dir\");

    // カレントディレクトリにあるfile2.txtをディレクトリsubdirに移動し、
    // ファイル名をfile.txtにリネームする
    Directory.CreateDirectory("subdir"); // 事前に移動先ディレクトリを作成しておく

    File.Move("file3.txt", @"subdir\file.txt");
  }
}

このメソッドでは既に存在するファイル名へリネームすることはできません。 したがって、Moveメソッドによるファイルのリネームではファイルの上書きが行われることはありません。 存在するファイル名へのリネームを行おうとした場合にはIOExceptionがスローされます。 また、移動先のディレクトリが存在しない場合にはDirectoryNotFoundExceptionがスローされるため、事前に移動先のディレクトリを作成しておく必要があります。

§3.4 ファイルのコピー (File.Copy)

ファイルをコピーするにはCopyメソッドを使用します。 三番目の引数overwritetrueを指定するとコピー先のファイルが存在する場合に上書きを許可します。 指定しなかった場合、もしくはfalseを指定した場合では、コピー先のファイルが存在する場合にはIOExceptionがスローされます。

ファイルをコピーする
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // カレントディレクトリにあるfile.txtをディレクトリbackup内のfile.bakにコピーする
    Directory.CreateDirectory("backup"); // 事前にコピー先ディレクトリを作成しておく

    File.Copy("file.txt", @"backup\file.bak");

    // カレントディレクトリにあるfile.txtをoldfile.txtというファイル名でコピーする
    // コピー先のファイルが存在する場合は上書きする
    File.Copy("file.txt", "oldfile.txt", true);

    try {
      // カレントディレクトリにあるfile.txtをoldfile.txtというファイル名でコピーする
      // コピー先のファイルが存在しても上書きを許可しない
      File.Copy("file.txt", "oldfile.txt", false);
    }
    catch (IOException) {
      // 既にoldfile.txtが存在するのでIOExceptionがスローされる
    }
  }
}

Moveメソッドと同様、コピー先のディレクトリが存在しない場合にはDirectoryNotFoundExceptionがスローされるため、事前にコピー先のディレクトリを作成しておく必要があります。

§3.5 ファイルの作成・オープン (File.Create, File.Open)

Fileクラスには既存のファイルを開くOpenメソッドや、新しくファイルを作成または上書きして開くCreateメソッドが用意されています。

ファイルを作成して文字列を書き込む
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ファイルfile.txtを作成して開く
    using (FileStream stream = File.Create("file.txt")) {
      // FileStreamに書き込むためのStreamWriterを作成
      using (StreamWriter writer = new StreamWriter(stream)) {
        // 文字列を改行付きで書き込む
        writer.WriteLine("Hello, world!");
      }
    }
  }
}

開いたファイルへの読み書きは、Createメソッド・Openメソッドが返すFileStreamを使って行います。 これらのメソッドとFileStreamについてはFileStreamクラスで解説しています。

開いたファイルを使ってテキストを読み書きする方法についてはStreamReaderクラス・StreamWriterクラス、バイナリの読み書きを行う方法についてはBinaryReaderクラス・BinaryWriterクラスを参照してください。

§3.6 ファイル属性

GetAttributesSetAttributesメソッドを使うことでファイル属性の取得・設定を行うことができます。 このメソッドでは、属性をブール型や整数型ではなくFileAttributes列挙体のビットごとの組み合わせによって表現します。

ファイルの属性を取得・設定する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // file.txtのファイル属性を取得する
    FileAttributes attr = File.GetAttributes("file.txt");

    // ファイル属性に読み取り専用属性を追加する
    attr = attr | FileAttributes.ReadOnly;

    // 変更したファイル属性をfile.txtに設定する
    File.SetAttributes("file.txt", attr);
  }
}

列挙体とビットごとの組み合わせについては列挙体とフラグ列挙体の基本と操作 §.値が特定のフラグを持つか (HasFlags)を参照してください。

Monoの場合、非Windows環境では読み取り専用の属性のみがエミュレートされ、それ以外の属性は無視されます。 ファイルパーミッションの変更を行いたい場合は、Mono.Unix.UnixFileInfoクラスのProtectionプロパティを使う必要があります。 Mono.Posix.dllを参照に追加することでこれらのクラスを使用することができます。

§3.7 タイムスタンプ

Fileクラスにはファイルのタイムスタンプを扱う以下のようなメソッドが用意されています。 ファイルだけでなくディレクトリのタイムスタンプを取得・設定する場合もこれらのメソッドを使うことができます。 同名のメソッドはDirectoryクラスにも用意されていて、同じくディレクトリだけでなくファイルのタイムスタンプを取得・設定できるようになっています。

Fileクラスのタイムスタンプを扱うメソッド
タイムスタンプの種類 取得用のメソッド 設定用のメソッド
作成日時 GetCreationTime
GetCreationTimeUtc
SetCreationTime
SetCreationTimeUtc
書き込み日時 GetLastWriteTime
GetLastWriteTimeUtc
SetLastWriteTime
SetLastWriteTimeUtc
アクセス日時 GetLastAccessTime
GetLastAccessTimeUtc
SetLastAccessTime
SetLastAccessTimeUtc
ファイルのタイムスタンプを取得する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // file.txtのタイムスタンプを取得する
    DateTime creation = File.GetCreationTime("file.txt");
    DateTime lastWrite = File.GetLastWriteTime("file.txt");
    DateTime lastAccess = File.GetLastAccessTime("file.txt");

    Console.WriteLine(creation);
    Console.WriteLine(lastWrite);
    Console.WriteLine(lastAccess);
  }
}

これらのメソッドではタイムスタンプをDateTime型で扱います。 タイムスタンプを整数値に変換したい場合はDateTime.ToFileTimeメソッドを使います。 また、UNIX時間に変換する方法についてはUNIX時間をDateTime型に変換するで解説しています。

末尾に〜UtcとついているメソッドではタイムスタンプをUTCでの日時で扱い、それ以外のメソッドではローカル時刻で扱います。 ローカル時刻・UTCについては時刻の種類・UTCとの時差・タイムゾーン間の変換を参照してください。

Monoの場合、UNIX環境ではGet/SetLastAccessTimeはatime(アクセス日時)、Get/SetLastWriteTimeはmtime(変更日時)を取得・設定します。 一方GetCreationTimeはGetLastWriteTimeと同じくmtimeを返しますが、SetCreationTimeの呼び出しでは何もしません。 また、Fileクラスのメソッドでは直接ctime(ステータス変更)の日時を取得・設定することはできません。

UNIX環境でファイルのタイムスタンプを設定・取得した場合
using System;
using System.IO;

class Sample {
  static void Main()
  {
    File.SetLastAccessTime("file.txt", new DateTime(2000, 1, 1, 0, 0, 0));
    File.SetLastWriteTime("file.txt", new DateTime(2000, 1, 2, 0, 0, 0));
    File.SetCreationTime("file.txt", new DateTime(2000, 1, 3, 0, 0, 0));

    Console.WriteLine(File.GetLastAccessTime("file.txt"));
    Console.WriteLine(File.GetLastWriteTime("file.txt"));
    Console.WriteLine(File.GetCreationTime("file.txt"));
  }
}
実行結果例
$ mcs sample.cs && mono sample.exe
01/01/2000 00:00:00
01/02/2000 00:00:00
01/02/2000 00:00:00
$ stat file.txt 
  File: `file.txt'
  Size: 0         	Blocks: 0          IO Block: 4096   通常の空ファイル
Device: fc02h/64514d	Inode: 396381      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/    smdn)   Gid: ( 1000/    smdn)
Access: 2000-01-01 00:00:00.000000000 +0900
Modify: 2000-01-02 00:00:00.000000000 +0900
Change: 2013-11-29 23:19:33.505223150 +0900
 Birth: -

§4 FileInfo・DirectoryInfo・DriveInfo

FileInfoクラスおよびDirectoryInfoクラスはファイル・ディレクトリを表すクラスです。 FileクラスDirectoryクラスを使った操作では常に操作対象となるファイル・ディレクトリのパスを引数に必要としますが、FileInfoクラス・DirectoryInfoクラスではインスタンス自体がファイル・ディレクトリを表し、常にインスタンスがパスを保持していることになり、操作を行う都度パスを指定する必要がなくなります。 Fileクラス・Directoryクラスはインスタンスを作成せずに使用することができますが、FileInfoクラス・DirectoryInfoクラスを使う場合は常にインスタンスを作成する必要があります。

両者の違いを比較するために、FileクラスとFileInfoクラスを使ってファイルの存在確認とコピーを行う例を挙げるとつぎのようになります。

FileクラスとFileInfoクラスを使った操作の違い
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // Fileクラスを使ってファイルが存在するか調べる
    if (File.Exists("file.txt")) {
      Console.WriteLine("file.txtは存在します");

      // file.txtをfile1.txtという名前でコピーする
      File.Copy("file.txt", "file1.txt");
    }

    // FileInfoクラスを使ってファイルが存在するか調べる
    FileInfo f = new FileInfo("file.txt");

    if (f.Exists) {
      Console.WriteLine("file.txtは存在します");

      // file.txtをfile2.txtという名前でコピーする
      f.CopyTo("file2.txt");
    }
  }
}

この例で使用しているExistsメソッドやCopyメソッド以外にも、Fileクラス・Directoryクラスに用意されているファイル・ディレクトリ操作のメソッドはFileInfoクラス・DirectoryInfoクラスにも用意されています。 そのため、どちらのクラスを使っても同じ操作が行えますが、操作を行う際にファイル名・ディレクトリ名を文字列として取り回すか、FileInfo・DirectoryInfoのインスタンスで取り回すかが異なるため、実装の都合に応じて使い分けることができます。

また、ドライブ情報を表すDriveInfoクラスも用意されています。 このクラスを使うと、システムに存在するドライブに関する情報(空き容量やドライブの種類、ルートディレクトリのパス)を取得することができます。 File・Directoryクラスとは異なり、DriveInfoクラスに対応するDriveのようなクラスは存在しないので、ドライブ情報を取得したい場合にはDriveInfoクラスを使う必要があります。

§4.1 FileInfoクラス

FileInfoクラスはファイルを表すクラスです。 FileInfoクラスのインスタンスを作成する際はファイル名を含むパスを指定する必要がありますが、相対パス・絶対パスのどちらでも指定することもできます。 また、存在しないファイルのインスタンスも作成することができます。

FileInfoインスタンスの作成
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ファイルE:\dir\file.txtを参照するFileInfoのインスタンスを作成する
    FileInfo f1 = new FileInfo(@"E:\dir\file.txt");

    // カレントディレクトリにあるファイルfile.txtを参照するFileInfoのインスタンスを作成する
    FileInfo f2 = new FileInfo("file.txt");
  }
}

FileInfoクラスのメソッド・プロパティとFileクラスPathクラスのメソッドの対応は次のとおりです。 FileInfoクラスのメソッド・プロパティの動作は、対応するFileクラス・Pathクラスのメソッドの動作と基本的には同じです。

FileInfoクラスのメソッド・プロパティ (抜粋)
操作 FileInfoクラスのメンバ 対応するFileクラス・Pathクラスのメソッド
コピー CopyToメソッド File.Copy
移動・リネーム MoveToメソッド File.Move
削除 Deleteメソッド File.Delete
ファイルが存在するか Existsプロパティ File.Exists
ファイルの作成 Createメソッド File.Create
ファイルのオープン Open/OpenRead/OpenWriteメソッド File.Open/OpenRead/OpenWrite
フルパスの取得 FullNameプロパティ Path.GetFullPath
ディレクトリ名の取得 DirectoryNameプロパティ Path.GetDirectoryName
ファイル名の取得 Nameプロパティ Path.GetFileName
拡張子の取得 Extensionプロパティ Path.GetExtension
ファイル属性の取得・設定 Attributesプロパティ File.GetAttributes/SetAttributes
タイムスタンプの取得・設定 CreationTime/CreationTimeUtcプロパティ
LastAccessTime/LastAccessTimeUtcプロパティ
LastWriteTime/LastWriteTimeUtcプロパティ
File.SetCreationTimeなど
ファイルサイズの取得 Lengthプロパティ (なし)
操作 FileInfoクラスのメンバ 対応するFileクラス・Pathクラスのメソッド

DirectoryNameプロパティはディレクトリ名を文字列で取得することができますが、Directoryプロパティを参照するとファイルが配置されているディレクトリをDirectoryInfoで取得することができるようになっています。

§4.2 DirectoryInfoクラス

DirectoryInfoクラスはディレクトリを表すクラスです。 FileInfoクラスと同様にDirectoryInfoクラスのインスタンスを作成する際はディレクトリ名を含むパスを指定する必要がありますが、相対パス・絶対パスのどちらでも指定することもできます。 また、存在しないディレクトリのインスタンスを作成することもできます。

DirectoryInfoインスタンスの作成
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ディレクトリE:\dir\を参照するDirectoryInfoのインスタンスを作成する
    DirectoryInfo d1 = new DirectoryInfo(@"E:\dir\");

    // カレントディレクトリにあるディレクトリsubdir1\subdir2を参照するDirectoryInfoのインスタンスを作成する
    DirectoryInfo d2 = new DirectoryInfo(@"subdir1\subdir2");
  }
}

DirectoryInfoクラスのメソッド・プロパティとDirectoryクラスPathクラスのメソッドの対応は次のとおりです。 DirectoryInfoクラスのメソッド・プロパティの動作は、対応するDirectoryクラス・Pathクラスのメソッドの動作と基本的には同じです。

DirectoryInfoクラスのメソッド・プロパティ (抜粋)
操作 DirectoryInfoクラスのメンバ 対応するDirectoryクラス・Pathクラスのメソッド
作成 Createメソッド Directory.CreateDirectory
サブディレクトリの作成 CreateSubdirectoryメソッド Directory.CreateDirectory
移動・リネーム MoveToメソッド Directory.Move
削除 Deleteメソッド Directory.Delete
ディレクトリが存在するか Existsプロパティ Directory.Exists
フルパスの取得 FullNameプロパティ Path.GetFullPath
ディレクトリ名の取得 Nameプロパティ Path.GetDirectoryName
タイムスタンプの取得・設定 CreationTime/CreationTimeUtcプロパティ
LastAccessTime/LastAccessTimeUtcプロパティ
LastWriteTime/LastWriteTimeUtcプロパティ
Directory.SetCreationTimeなど
操作 FileInfoクラスのメンバ 対応するFileクラス・Pathクラスのメソッド

DirectoryInfoクラスでは、親ディレクトリのDirectoryInfoをParentプロパティ、ルートディレクトリのDirectoryInfoをRootプロパティでそれぞれ取得することができます。

§4.2.1 DirectoryInfoクラスを使ったディレクトリの走査

DirectoryクラスのGetFiles・EnumerateFilesメソッドでは見つかったファイルの名前が文字列で返されますが、DirectoryInfoクラスのGetFilesEnumerateFilesメソッドでは文字列ではなくFileInfoクラスが返されます。

DirectoryInfoクラスを使ったディレクトリの走査
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // Directoryクラスを使ってディレクトリE:\dirにあるすべてのファイルを取得して列挙する
    foreach (string file in Directory.GetFiles(@"E:\dir\")) {
      Console.WriteLine(file);
    }

    foreach (string file in Directory.EnumerateFiles(@"E:\dir\")) {
      Console.WriteLine(file);
    }

    // DirectoryInfoクラスを使ってディレクトリE:\dirにあるすべてのファイルを取得して列挙する
    DirectoryInfo d = new DirectoryInfo(@"E:\dir\");

    foreach (FileInfo file in d.GetFiles()) {
      Console.WriteLine(file.Name);
    }

    foreach (FileInfo file in d.EnumerateFiles()) {
      Console.WriteLine(file.Name);
    }
  }
}

同様にDirectoryInfoクラスのGetDirectoriesEnumerateDirectoriesメソッドでは見つかったディレクトリはDirectoryInfoで返されます。 GetFileSystemInfosEnumerateFileSystemInfosメソッドではFileSystemInfoで返されます。

§4.3 FileSystemInfoクラス

FileSystemInfoクラスGetFileSystemInfosEnumerateFileSystemInfosメソッドの戻り値の型に使用されるクラスです。 このクラスはFileInfoクラスおよびDirectoryInfoクラスに共通する基底クラスです。 GetFileSystemInfos・EnumerateFileSystemInfosでは見つかったファイルのFileInfoおよびサブディレクトリのDirectoryInfoが返されます。

DirectoryInfo.EnumerateFileSystemInfosメソッドを使ったディレクトリの走査とFileSystemInfoの型チェックの例
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // DirectoryInfo.EnumerateFileSystemInfosメソッドでディレクトリE:\dir直下にある
    // すべてのファイルとサブディレクトリを取得して列挙する
    DirectoryInfo d = new DirectoryInfo(@"E:\dir\");

    foreach (FileSystemInfo i in d.EnumerateFileSystemInfos()) {
      if (i is FileInfo)
        Console.WriteLine("{0}はファイルです", i.Name);
      else if (i is DirectoryInfo)
        Console.WriteLine("{0}はサブディレクトリです", i.Name);
    }
  }
}
実行結果例
file1.txtはファイルです
file2.txtはファイルです
file3.txtはファイルです
subdir1はサブディレクトリです
subdir2はサブディレクトリです

FileSystemInfoクラスにはファイルまたはディレクトリの名前を取得するNameプロパティ、フルパスを取得するFullNameプロパティ、実際に存在するかどうかを取得するExistsプロパティなどFileInfoクラスDirectoryInfoクラスに共通するプロパティが用意されています。

§4.4 DriveInfoクラス

DriveInfoクラスはシステムに存在する論理ドライブの情報を取得するためのクラスです。 コンストラクタでドライブ名を指定してインスタンスを作成するか、GetDrivesメソッドによって存在するすべての論理ドライブのDriveInfoを取得することができます。 DriveTypeプロパティを参照するとドライブの種類(光学ドライブ・固定ディスク・ネットワーク・リムーバブルデバイスなど)を取得することができます。

システム上のドライブとドライブ種別の列挙
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // CドライブのDriveInfoを作成する
    DriveInfo c = new DriveInfo("C");

    // システムに存在するすべてのドライブを取得する
    DriveInfo[] drives = DriveInfo.GetDrives();

    foreach (DriveInfo drive in drives) {
      // ドライブ名とドライブの種類を表示する
      Console.WriteLine("{0}: {1}", drive.Name, drive.DriveType);
    }
  }
}
Windows上の.NET Frameworkでの実行結果例
A:\: Removable
C:\: Fixed
E:\: Fixed
F:\: Fixed
Q:\: CDRom
R:\: CDRom
S:\: Network
T:\: Network

Linux上のMonoではドライブの代わりにマウントされているディレクトリが列挙されます。

システム上のドライブとドライブ種別の列挙(Linux+Monoの場合)
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (DriveInfo drive in DriveInfo.GetDrives()) {
      Console.WriteLine(drive.Name);
    }
  }
}
Linux上のMonoでの実行結果例
/
/boot
/home
/srv/www
/srv/mail

DriveInfoクラスにはこの他にも、ボリュームラベルを取得するVolumeLabel、ファイルシステムの種類(NTFSなど)を文字列形式で返すDriveFormat、ドライブの容量を取得するTotalSize, TotalFreeSpace, AvailableFreeSpaceなどのプロパティがありますが、これらの値はいずれもドライブの準備ができていない場合(CDが挿入されていないなど)に取得しようとするとIOExceptionをスローします。 メディアが使用可能か・ドライブの準備ができているかを調べるにはIsReadyプロパティを参照します。

ドライブの列挙とドライブ情報の取得
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // システムに存在するすべてのドライブを取得する
    DriveInfo[] drives = DriveInfo.GetDrives();

    foreach (DriveInfo drive in drives) {
      // ドライブ名とドライブの種類を表示する
      Console.Write("{0}: {1}", drive.Name, drive.DriveType);

      if (drive.IsReady)
        // ドライブが使用可能な場合は、ドライブの情報を取得する
        Console.WriteLine(" ({0}, {1:N0} bytes)", drive.DriveFormat, drive.TotalSize);
      else
        Console.WriteLine();
    }
  }
}
Windows上の.NET Frameworkでの実行結果例
A:\: Removable
C:\: Fixed (NTFS, 80,024,170,496 bytes)
E:\: Fixed (NTFS, 81,961,938,944 bytes)
F:\: Fixed (NTFS, 1,000,202,039,296 bytes)
Q:\: CDRom
R:\: CDRom
S:\: Network (NTFS, 147,640,692,736 bytes)
T:\: Network (NTFS, 499,962,740,736 bytes)
Linux上のMonoでの実行結果例
/: Fixed (ext, 21,137,846,272 bytes)
/boot: Fixed (ext, 1,059,500,032 bytes)
/home: Fixed (ext, 10,568,916,992 bytes)
/srv/www: Fixed (ext, 98,430,779,392 bytes)
/srv/mail: Fixed (ext, 98,430,779,392 bytes)

DriveInfoクラスには光学ドライブのトレー開閉、メディアの取り外し・接続を行ったりするような操作を行うメソッドは用意されていません。 そういった操作を行う例はCD-ROM・リムーバブルメディアを取り出すで紹介しています。