C#プログラミングTips

ファイルとディレクトリの操作

デジタルアドバンテージ
2002/03/07


 今回は、ファイルやディレクトリの操作で必要となる.NET Frameworkの機能を紹介し、サンプル・プログラムとして「touchコマンドもどき」を作成してみる。touchコマンドは、UNIXやLinuxでは標準的なコマンドで、ファイルのタイムスタンプを更新するためのものだ。このコマンドは、Windowsには標準では提供されない。確かに、Windows環境ではあまり必要にはならないが、アプリケーションを客先に納品する場合など、すべてのファイルのタイムスタンプを一括して更新したい場合などには必要になる。このような処理を行うには、指定されたディレクトリ内の各ファイルに対して、ファイルが持つ属性を操作することになる。

 .NET Frameworkでは、ディレクトリに対する操作はDirectoryクラス、ファイルに対する操作はFileクラスにまとめられている。なお、ファイルに対する操作といっても、ファイルを開き、内容を読み書きする機能ではない。そのような処理については「ファイル入出力の基礎」などで解説している。

Directoryクラス

 ディレクトリに対する操作はすべてDirectoryクラス(System.IO名前空間)にまとめられている。このクラスと次に説明するFileクラスが持つメソッド群はすべて静的(static)なメソッドである。このためNewによってインスタンスを作成しなくても直接呼び出し可能なユーティリティ的なクラスである。

 Directoryクラスによって提供される主な処理機能をまとめると次のようになる。

  • ディレクトリの作成/削除/移動
  • カレント・ディレクトリの取得/設定
  • ディレクトリの作成日時、更新日時、アクセス日時の取得/設定
  • ディレクトリ内のサブディレクトリ/ファイルの取得

 ディレクトリの更新日時、アクセス日時についてはあまりなじみがないかもしれないが、これらはエクスプローラでも[表示]メニューの[詳細表示の設定]を変更することで参照することができる。

再帰的なディレクトリ表示

 次の例は、指定されたディレクトリに存在するすべてサブディレクトリを取得するDirectory.GetDirectoriesメソッドを使用したサンプル・プログラムだ。このプログラムは、特定ディレクトリ以下すべてのサブディレクトリのディレクトリ名を再帰的に取得し、画面に表示する。

 1: // dirs.cs
 2:
 3: using System;
 4: using System.IO;
 5:
 6: public class Dirs {
 7:   public static void listing(string startdir) {
 8:     foreach (string dir
 9:               in Directory.GetDirectories(startdir)) {
10:       Console.WriteLine(dir);
11:       listing(dir);
12:     }
13:   }
14:
15:   public static void Main() {
16:     listing("c:\\");
17:   }
18: }
サブディレクトリのディレクトリ名を再帰的に表示するプログラム

 Directory.GetDirectoriesメソッドは、パラメータで指定されたディレクトリに含まれるサブディレクトリ名を文字列の配列として返す。

 ディレクトリ名とともに各ディレクトリに含まれているファイルも表示する場合には、上記のlistingメソッドに、Directory.GetFilesメソッドを使用したファイル名表示のコードを追加すればよい。

public static void listing(string startdir) {
   foreach (string file in Directory.GetFiles(startdir)) {
     Console.WriteLine(file);
   }
   foreach (string dir in Directory.GetDirectories(startdir)) {
     Console.WriteLine(dir);
     listing(dir);
   }
}
ディレクトリに含まれるファイルも表示されるようにしたlistingメソッド

 なお、今回作成したtouchコマンドでは、これら2つのメソッドは使用せずに、指定したディレクトリに含まれるすべてのファイルとサブディレクトリを取得するDirectory.GetFileSystemEntriesメソッドを用い、得られたものがディレクトリかファイルかをファイル属性によって判断する方法をとっている。

Fileクラス

 ファイル属性の取得をはじめ、ファイルに関する操作を実行するにはFileクラス(System.IO名前空間)を使用する。Fileクラスによって提供される主な機能は次のようになっている。

  • ファイルのコピー/作成/削除/移動
  • ファイルおよびディレクトリの属性の取得/設定
  • ファイルの作成日時、更新日時、アクセス日時の取得/設定
  • ファイルのオープン

 ファイル/ディレクトリ属性とは、隠しファイルや読み取り専用(read-only)ファイル、システム・ファイルなどの属性である。ファイルやディレクトリが持つ属性一覧はFileAttributes列挙体(enum)で定義されている。

 最後に挙げた「ファイルのオープン」は、FileStreamクラスStreamReaderクラス(ともにSystem.IO名前空間)でファイルをオープンするのと実質的に同じで、単にあらかじめよく使用されるコードのパターンを1つのメソッドにまとめているだけである。

 比較的よく必要になると予想されるファイル・サイズの情報については、Fileクラスのメソッドでは取得できない。これにはFileクラスとほぼ同等の機能を提供するFileInfoクラスLengthメソッドを利用する。静的メソッドしか持たないFileクラスと異なり、FileInfoクラスはインスタンス・メソッド(呼び出しにインスタンスが必要となるメソッド)しか持たない。つまり、コンストラクタで対象となるファイルのファイル名を指定し、まずインスタンスを生成してから、そのファイルに対して各種メソッドにより操作を行う。なお、Directoryクラスについても同様に、DirectoryInfoクラスが存在する。

touchコマンドの作成

 続いてtouchコマンドの作成に入ろう。本家のtouchコマンドは多くのコマンドライン・オプションを持っているが、今回はあくまでプログラミングのサンプルということで、プログラムを実行したディレクトリ配下のすべてのファイルとサブディレクトリについて、その更新日時を現在時刻に設定するというだけの機能に絞る。次に示すコードがこのプログラムの全ソース・コードだ。

 1: // touch.cs
 2:
 3: using System;
 4: using System.IO;
 5:
 6: public class TouchClass {
 7:   static DateTime now = DateTime.Now;
 8:
 9:   public static void touch(string startdir) {
10:     foreach (string entries
11:               in Directory.GetFileSystemEntries(startdir)) {
12:       if (File.GetAttributes(entries)
13:             == FileAttributes.Directory) {
14:         try {
15:           Directory.SetLastWriteTime(entries, now);
16:         } catch (Exception e) {
17:           Console.WriteLine("{0}\n\t{1}", entries, e.Message);
18:         }
19:         touch(entries);
20:       } else {
21:         try {
22:           File.SetLastWriteTime(entries, now);
23:         } catch (Exception e) {
24:           Console.WriteLine("{0}\n\t{1}", entries, e.Message);
25:         }
26:       }
27:     }
28:   }
29:
30:   public static void Main() {
31:     //now = DateTime.Parse("2001/1/1");
32:     touch(Directory.GetCurrentDirectory());
33:   }
34: }
サンプル・プログラムtouch.csのソース・コード

 プログラムではtouchメソッドを再帰的に呼び出すが、そのスタートとなるカレント・ディレクトリはDirectory.GetCurrentDirectoryメソッドにより取得している(32行目)。

 すでに述べたとおり、あるディレクトリに含まれているすべてのファイルとサブディレクトリは、Directory.GetFileSystemEntriesメソッドにより取得する(11行目)。続いてFile.GetAttributesメソッドによりファイルの属性を取得し(12行目)、これがFileAttributes.Directoryであれば、それがディレクトリであると判断できる(13行目)。

 更新日時の設定は、ディレクトリに対してはDirectory.SetLastWriteTimeメソッド(15行目)、ファイルに対してはFile.SetLastWriteTimeメソッド(22行目)を用いる。ほかのプログラムがファイルを使用中だったり、ファイルが読み取り専用だったりして、更新日時の設定ができないファイルに遭遇してもプログラムが異常終了しないように、この2つのメソッドの実行に関しては例外のハンドリングが必要となる。例外が発生した場合には、更新日時を設定できなかったファイルのファイル名(あるいはディレクトリ名)と、その際に発生した例外についての説明(ExceptionオブジェクトMessageプロパティ)を表示し(17行目および24行目)、そのまま処理を継続するようにしている。

 更新日時に使用する値は、7行目でDateTime.Nowプロパティにより現在の日時としているが、31行目(コメントアウトしている)のように、特定の日時を使用するように変更することも簡単だ。

 touchコマンドには、パラメータで指定したファイルが存在しなければ、サイズ0でそのファイルを作成するという機能などもある。時間があればフル機能のC#版touchに挑戦していただきたい。C#初心者の方にとってはよい演習課題となるだろう。End of Article

関連記事(Insider.NET内)
ファイル入出力の基礎
 
「C#プログラミングTips」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間