rksoftware

Visual Studio とか C# とかが好きです

.NET でのディレクトリの扱い方

.NET/C# でファイルパスを扱う次のようなコードを見たことは無いでしょうか?

string directory = "directory1\\directory2\\";
string filename = "filename.ext";
string combined = "";

if (directory.EndsWith("\\"))
{
    combined = directory.Substring(0, directory.Length - 1) + "\\" + filename;
}
else
{
    combined = directory + "\\" + filename;
}

Console.WriteLine(combined);

見たことがない? 素晴らしい。私は何十回と見てきました。

■ 結論

前述のコードは、Windows 環境でしか動きません。Windows のディレクトリ区切り文字は \ ですが、Linux や mac では / のため意図通りのパスになりません。
.NET は当初よりその差を吸収してくれる API を持っています。System.IO 名前空間のクラス達がいい感じに扱ってくれます。

■ 今こそ System.IO.Path.Combine

前述のコードは、「.NET Framework は Windows でしか動作させないのだから、Windows でしか動作しないコードを書く!」という強い意志を感じます。実際、書いたコードを Windows 以外で動作することは全く考える必要がない場面が大半だったのは事実でしょう。

しかし時代は変わりました。

.NET 初期から考えられていたマルチプラットフォームが遂に当たり前になってきました。そう、.NET Core/.NET Standard です。 今こそ改めて、ファイルパスの扱い方を見直す時です。

■ コード例

// ディレクトリ区切り文字
var directorySeparatorChar = System.IO.Path.DirectorySeparatorChar;
Console.WriteLine($"ディレクトリ区切り文字 : {directorySeparatorChar}");

// ディレクトリとファイルの結合
var combined1 = System.IO.Path.Combine("directoryname", "filename.ext");
Console.WriteLine($"ディレクトリとファイルの結合 : {combined1}");

// ディレクトリ複数との結合
var combined2 = System.IO.Path.Combine("d1", "d2", "filename.ext");
Console.WriteLine($"ディレクトリ複数との結合 : {combined2}");

// パスからディレクトリを取得
var directory = System.IO.Path.GetDirectoryName(combined2);
Console.WriteLine($"パスからディレクトリを取得 : {directory}");

// パスからファイル名を取得
var filename = System.IO.Path.GetFileName(combined2);
Console.WriteLine($"パスからファイル名を取得 : {filename}");

// ディレクトリを分解
var directories = GetDirectories(directory).ToArray();
Console.WriteLine($"ディレクトリを分解 : {string.Join(", ", directories)}");
IEnumerable<string> GetDirectories(string directoryname)
{
    var directoryInfo = new System.IO.DirectoryInfo(directoryname);
    while (directoryInfo != null)
    {
        yield return directoryInfo.Name;
        directoryInfo = directoryInfo.Parent;
    }
}

□ Windows での実行結果例

ディレクトリ区切り文字 : \
ディレクトリとファイルの結合 : directoryname\filename.ext
ディレクトリ複数との結合 : d1\d2\filename.ext
パスからディレクトリを取得 : d1\d2
パスからファイル名を取得 : filename.ext
ディレクトリを分解 : d2, d1, netcoreapp2.1, Debug, bin, ConsoleApp1, ConsoleApp1, C:\

□ mac での実行結果例

ディレクトリ区切り文字 : /
ディレクトリとファイルの結合 : directoryname/filename.ext
ディレクトリ複数との結合 : d1/d2/filename.ext
パスからディレクトリを取得 : d1/d2
パスからファイル名を取得 : filename.ext
ディレクトリを分解 : d2, d1, ConsoleApp1, ConsoleApp1, /

今書いているコードもいつ Linux などで動作させる日が来るか分かりません。.NET Framework のプロジェクトのコードであっても、知らないところでコピー&ペーストで再利用されるかもしれません。
できるだけマルチプラットフォームを意識して書いていきましょう。