[C#] 44. ファイル(FileInfo)とディレクトリ(DirectoryInfo)を扱い


Study / C#    作成日付 : 2021/10/05 19:29:34   修正日付 : 2021/10/05 19:29:34

こんにちは。明月です。


この投稿はC#でファイル(FileInfo)とディレクトリ(DirectoryInfo)を扱いに関する説明です。


以前の投稿でIOを通ってファイルを作成して読み込む方法に関して説明しました。

link - [C#] 42. ファイルを扱い(IO)とファイルメタデータ(FileInfo)を使い方


今度はファイルを作成して読み込みより、ディレクトリとファイルのメタデータを利用してディレクトリとファイルに関する情報を取得して探索する方法に関して説明します。

FileInfoクラスはファイルに関するメタデータ、つまり、ファイルのパス、サイズ、作成日時、修正日時に関する情報があります。

ディレクトリは実際にバイナリデータで存在することではなく、OSで管理するメタデータです。FileInfoとみたいにDirectoryInfoがありますが、ディレクトリ(フォルダ)に関する情報があります。

using System;
using System.IO;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // ディレクトリのメタデータ取得
      var dirinfo = new DirectoryInfo("d:\\work");
      // ディレクトリのプール名
      Console.WriteLine("DirectoryInfo Name - " + dirinfo.FullName);
      // ディレクトリの作成日時
      Console.WriteLine("DirectoryInfo CreationTime - " + dirinfo.CreationTime);
      // ディレクトリの更新日時
      Console.WriteLine("DirectoryInfo LastWriteTime - " + dirinfo.LastWriteTime);
      // ディレクトリのアクセス日時
      Console.WriteLine("DirectoryInfo LastAccessTime - " + dirinfo.LastAccessTime);

      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


ファイルのメタファイルみたいにディレクトリのメタ情報を取得することができます。

DirectoryInfoにはFileinfoと違い、ディレクトリを追加、削除があります。

ファイル生成は当該なファイルに関するバイナリデータが必要ですが、ディレクトリの場合はメタデータ追加、削除だけで扱えるからです。

using System;
using System.IO;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // ディレクトリのメタデータを取得
      var dirinfo = new DirectoryInfo("d:\\work\\abc");
      // ディレクトリに追加
      dirinfo.Create();      

      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


ディレクトリが追加されました。

using System;
using System.IO;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // ディレクトリのメタデータを取得
      var dirinfo = new DirectoryInfo("d:\\work\\abc");
      // ディレクトリに削除
      dirinfo.Delete();      

      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


ディレクトリが削除されました。


そしてFileInfoとDirectoryInfoの場合はインスタンスを生成して使わなければならないですが、毎度、メタデータを使うためにインスタンスを生成することには気になります。

それでstaticクラスでもメタデータを取得することができます。

using System;
using System.IO;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // ディレクトリ存在可否
      Console.WriteLine("Directory.Exists - " + Directory.Exists("d:\\work"));
      // ファイル存在可否
      Console.WriteLine("File.Exists - " + File.Exists("d:\\work\\test.txt"));

      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}


Existsの関数はFileInfoとDirectoryInfoにもある関数ですが、FileとDirectoryのstaticクラスにもメタデータを使えます。


このFileとDirectoryクラスで単純にファイルとディレクトリのメタ情報を取得するクラスですが、探索アルゴリズムを利用して作成すれば簡単なファイル探索プログラムを作成することができます。

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Example
{
  // ディレクトリとファイルの情報クラス
  class DirNode
  {
    // ディレクトリのパス
    public string Path { get; private set; }
    // サブディレクトリ
    public List<DirNode> Dirs { get; private set; }
    // サブファイル
    public List<string> Files { get; private set; }
    // コンストラクタ
    public DirNode(string path)
    {
      // インスタンスを生成
      this.Dirs = new List<DirNode>();
      this.Files = new List<string>();
      this.Path = path;
    }
  }
  class Program
  {
    // ディレクトリの情報を取得関数
    static DirNode GetDirectoryInfo(string path)
    {
      // ディレクトリが存在がなければ
      if (!Directory.Exists(path))
      {
        // nullリターン
        return null;
      }
      // ディレクトリとファイルの情報クラスのインスタンスを生成
      var dir = new DirNode(path);
      // サブディレクトリの情報取得
      var subdirs = Directory.GetDirectories(path);
      // 繰り返しでサブディレクトリを探索
      foreach (var d in subdirs)
      {
        // サブディレクトリを探索(再帰関数)
        dir.Dirs.Add(GetDirectoryInfo(d));
      }
      // サブファイルの情報を取得
      dir.Files.AddRange(Directory.GetFiles(path));
      return dir;
    }
    // コンソールに出力するStringデータ作成
    static void BuildPrintString(DirNode dir, StringBuilder sb, int space = 0, string replace = null)
    {
      // スペース位置設定
      var pos = space;
      // 改行になればスペースを入れ、現在位置
      var insertSpace = new Action(() =>
      {
        // 改行
        sb.AppendLine();
        // 位置程にスペースを入れる
        for (int j = 0; j < pos; j++)
        {
          // バッファーにスペースを書き込む。
          sb.Append(" ");
        }
      });
      // ルートディレクトリを置換
      var buf = replace != null ? dir.Path.Replace(replace, "") : dir.Path;
      // バッファーに書き込む。
      sb.Append(buf);
      // スペース位置を追加
      pos = pos + buf.Length;
      // サブディレクトリがあれば
      if (dir.Dirs.Count > 0)
      {
        // サブディレクトリ程に探索
        for (int i = 0; i < dir.Dirs.Count; i++)
        {
          // 二つ目から改行
          if (i != 0)
          {
            // 改行 + スペース
            insertSpace();
          }
          // 探索マークをバッファーに書き込む
          sb.Append("  =>  ");
          // 再帰関数で探索
          BuildPrintString(dir.Dirs[i], sb, pos + 10, dir.Path);
        }
        // 改行 + スペース
        insertSpace();
      }
      // サブファイルがあれば
      if (dir.Files.Count > 0)
      {
        // サブファイル程に探索
        for (int i = 0; i < dir.Files.Count; i++)
        {
          // 二つ目から改行
          if (i != 0)
          {
            // 改行 + スペース
            insertSpace();
          }
          // 探索マークをバッファーに書き込む
          sb.Append("  =>  ");
          // ファイル名をバッファーに書き込む
          sb.Append(dir.Files[i].Replace(dir.Path, ""));
        }
      }
    }
    // 実行関数
    static void Main(string[] args)
    {
      // d:\\workディレクトリの情報取得(サブディレクトリを含め)
      var dir = GetDirectoryInfo("d:\\work");
      // StringBuilderインスタンスを生成
      var sb = new StringBuilder();
      // コンソールに出力するStringのデータを作成
      BuildPrintString(dir, sb);
      // コンソールに出力
      Console.WriteLine(sb.ToString());
      // 任意のキーを押してください
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}




上の例でディレクトリからサブディレクトリを呼び出す時にはディレクトリ探索する再帰関数を通ってサブディレクトリとサブファイルを探索します。

再帰関数というのはstack interruptが掛ける作業なので、性能が遅くなるし、overflow可能性もあるので、効率性が悪いですが、ソースがすごく見やすくみえるし、この探索アルゴリズムを作る時にしやすく設計と実装が可能なのでよく使います。


ここまでC#でファイル(FileInfo)とディレクトリ(DirectoryInfo)を扱いに関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿