パスからファイル名・ディレクトリ名を抽出したり、パスを結合したりするにはPathクラスを使います。 .NETではパスは文字列として扱うため、パス関連の文字列処理を行う専用クラスとしてPathクラスが用意されています。 Pathクラスでは、ファイル名・ディレクトリ名・拡張子といったパスからの要素の抽出、絶対パス・相対パスなどのパス同士の結合絶対パス・相対パス間の変換やパス形式の検証などの操作を行うことができます。

URL(URI)を解析済みの状態で保持するUriクラスなどとは異なり、Pathクラスはパス自体は保持せず、パスに関する文字列操作のみを提供するクラスとなっています。 そのため、Pathクラスではインスタンスを作成せず、常にクラスメソッドを呼び出すことによりパス文字列に対する処理を行います。

Stringクラスのメソッドによる文字列操作でもパスからファイル名・ディレクトリ名の抽出、パス同士の結合といった操作を行うことはできますが、Pathクラスでは...などを含む相対パス、プラットフォームごとに異なるディレクトリ区切り文字(\/)の扱い、大文字小文字の違い(file.txtFILE.TXT)、ディレクトリ区切り文字終端の有無(dir\dir)などを適切に扱えるようになっているため、パス操作に関するある程度の考慮をPathクラスに任せることができます。

Pathクラスでは、Windows形式(DOS)のパス、UNIX形式のパスを扱うことができるほか、Windows上ではUNCパス、DOSデバイスパスがサポートされます。 (§.Pathクラスでサポートされるパス形式)

この文書では用語としてフォルダ(folder)は用いず、Windows以外やAPI名等で共通して使われるディレクトリ(directory)を使用します。

導入

ここではPathクラスを使うにあたってよく用いられる逐語的文字列リテラルと、Pathクラスで扱えるパス形式、Pathクラスでは直接扱えないfileスキーム形式でのパスについて触れておきます。

逐語的文字列リテラルによるパスの記述

Pathクラスを含め、.NETではパスをそのままの文字列で扱います。 一方で、Windowsではパス内のディレクトリ区切り記号に\が用いられます。 C#では文字列中で\を用いるとエスケープ記号として扱われるため、ディレクトリ区切り文字として記述するには\\のように二つ重ねる必要があります。

\を多用するパスを記述する際は必然的にエスケープが多くなり読みづらくなりますが、C#では逐語的文字列リテラルを用いることで\をエスケープせずにそのまま記述することができます。

文字列リテラル"..."にアットマーク@を前置して逐語的文字列リテラル@"..."として記述すると\はエスケープ記号としては扱われなくなり、そのまま記述できるようになります。

通常の文字列と逐語的文字列リテラルでパスを記述する
using System;

class Sample {
  static void Main()
  {
    // 通常の文字列リテラルを使って記述したパス
    // (\はエスケープ記号として扱われるため、ディレクトリ区切り文字は\\と記述する必要がある)
    var path1 = "C:\\Windows\\Microsoft.NET\\Framework";

    // 逐語的文字列リテラルを使って記述したパス
    // (\は通常の文字として扱われるので、ディレクトリ区切り文字をそのまま記述できる)
    var path2 = @"C:\Windows\Microsoft.NET\Framework";

    // path1とpath2はどちらも同じ文字列となる
    Console.WriteLine(path1);
    Console.WriteLine(path2);
  }
}
実行結果
C:\Windows\Microsoft.NET\Framework
C:\Windows\Microsoft.NET\Framework

Pathクラスでサポートされるパス形式

Pathクラスでは、Windows形式のパス(DOSパス)、UNCパス、DOSデバイスパスなどがサポートされています。 また、ディレクトリ区切り記号として\および/の両方(非Windows環境では/のみとなる)を処理できることから、UNIX形式のパスを扱うこともできます。

Windows形式・UNIX形式のパスでは相対パスを扱うことができますが、常に完全修飾されたパスとなるUNCパス・DOSデバイスパスでは相対パスを扱うことはできません。

Pathクラスで扱うことのできるパス形式とパスの例
パス形式 パスの例 備考
DOSパス E:\dir\file.txt 絶対パス 非Windows環境では、ドライブ部分もディレクトリ名(またはファイル名)の一部として解釈される
E:/dir/file.txt ディレクトリ区切り記号に/を用いた絶対パス
E:dir\file.txt Eドライブのカレントディレクトリからの相対パス
\dir\file.txt カレントドライブからの相対パス
(カレントドライブのルートディレクトリを基準とするパス)
dir\file.txt カレントディレクトリからの相対パス
.\dir\..\file.txt カレントディレクトリからの相対パス
/dir/file.txt ディレクトリ区切り記号に/を用いた相対パス
UNIX形式の絶対パス
./dir/../file.txt ディレクトリ区切り記号に/を用いた相対パス
UNIX形式の相対パス
UNCパス \\LOCALHOST\e$\dir\file.txt ローカルホストのEドライブをルートとするパス Windows環境でのみサポートされる
\\127.0.0.1\e$\dir\file.txt ホスト127.0.0.1のEドライブをルートとするパス
\\SERVER1\Shared\dir\file.txt ホストSERVER1のボリュームSharedをルートとするパス
DOSデバイスパス \\.\E:\dir\file.txt Eドライブをルートとするパス .NET Core 1.1以降、.NET Framework 4.6.2以降かつ、Windows環境でのみサポートされる
\\.\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\dir\file.txt GUIDに一致するボリュームをルートとするパス
\\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\dir\file.txt GUIDに一致するボリュームをルートとするパス
\\.\UNC\LOCALHOST\e$\dir\file.txt ローカルホストのEドライブをルートとするパス
パス形式 パスの例 備考

file URI schemeとパス

file:で表されるパス(file URI scheme形式のURI)はPathクラスでは直接扱うことはできません。 file:http:などのURLと同様にUriクラスで扱います。

次の例のようにUri.HostプロパティUri.LocalPathプロパティでURIからホスト名部とパス部を分離し、パス部をPathクラスで処理することにより、file:形式のパスを扱うことができます。

file URI scheme形式のURIからパス部分を取得して、Pathクラスで処理する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    const string format = "{0,10} |{1,40} |{2,20} |{3,20} |{4,10} | {5}";

    Console.WriteLine(format, "Host", "LocalPath", "Root", "Directory", "File", "URL");
    Console.WriteLine(new string('-', 150));

    foreach (var url in new[] {
      "file:///e:/dir/subdir/file.txt",
      "file:///dir/subdir/file.txt",
      "file://127.0.0.1/e:/dir/subdir/file.txt",
      "file://server1/shared/dir/subdir/file.txt",
    }) {
      var uri = new Uri(url);

      Console.WriteLine(
        format,
        uri.Host,
        uri.LocalPath,
        Path.GetPathRoot(uri.LocalPath),
        Path.GetDirectoryName(uri.LocalPath).Substring(Path.GetPathRoot(uri.LocalPath).Length),
        Path.GetFileName(uri.LocalPath),
        uri
      );
    }
  }
}
file URI scheme形式のURIからパス部分を取得して、Pathクラスで処理する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()

    Const format As String = "{0,10} |{1,40} |{2,20} |{3,20} |{4,10} | {5}"

    Console.WriteLine(format, "Host", "LocalPath", "Root", "Directory", "File", "URL")
    Console.WriteLine(New String("-"c, 150))

    For Each url As String In New String() {
      "file:///e:/dir/subdir/file.txt",
      "file:///dir/subdir/file.txt",
      "file://127.0.0.1/e:/dir/subdir/file.txt",
      "file://server1/shared/dir/subdir/file.txt"
    }
      Dim uri As New Uri(url)

      Console.WriteLine(
        format,
        uri.Host,
        uri.LocalPath,
        Path.GetPathRoot(uri.LocalPath),
        Path.GetDirectoryName(uri.LocalPath).Substring(Path.GetPathRoot(uri.LocalPath).Length),
        Path.GetFileName(uri.LocalPath),
        uri
      )
    Next
  End Sub
End Class
実行結果
      Host |                               LocalPath |                Root |           Directory |      File | URL
------------------------------------------------------------------------------------------------------------------------------------------------------
           |                  e:\dir\subdir\file.txt |                 e:\ |          dir\subdir |  file.txt | file:///e:/dir/subdir/file.txt
           |                    /dir/subdir/file.txt |                   \ |          dir\subdir |  file.txt | file:///dir/subdir/file.txt
 127.0.0.1 |      \\127.0.0.1\e:\dir\subdir\file.txt |      \\127.0.0.1\e: |         \dir\subdir |  file.txt | file://127.0.0.1/e:/dir/subdir/file.txt
   server1 |    \\server1\shared\dir\subdir\file.txt |    \\server1\shared |         \dir\subdir |  file.txt | file://server1/shared/dir/subdir/file.txt

パスの分割・要素の抽出

ここではPathクラスのメソッドを使って、パスからディレクトリ名・ファイル名や拡張子などを抽出する方法について解説します。

ファイル名・ディレクトリ名 (GetFileName/GetDirectoryName)

パスからファイル名を抽出するにはGetFileNameメソッド、ディレクトリ名を抽出するにはGetDirectoryNameメソッドを使います。

GetFileName・GetDirectoryNameメソッドでは、パスに含まれる一番最後のディレクトリ区切り文字の位置を区切りとして、ファイル名・ディレクトリ名を抽出します。 パスに該当する部分がないと判断される場合は、空の文字列が返されます。

Path.GetFileName/GetDirectoryNameメソッドでファイル名/ディレクトリ名を抽出する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"file.txt"                 // ファイル名のみのパス
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("file = {0}", Path.GetFileName(path)); // パスからファイル名を抽出
      Console.WriteLine("directory = {0}", Path.GetDirectoryName(path)); // パスからディレクトリ名を抽出
      Console.WriteLine();
    }
  }
}
Path.GetFileName/GetDirectoryNameメソッドでファイル名/ディレクトリ名を抽出する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "file.txt"                ' ファイル名のみのパス
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("file = {0}", Path.GetFileName(p)) ' パスからファイル名を抽出
      Console.WriteLine("directory = {0}", Path.GetDirectoryName(p)) ' パスからディレクトリ名を抽出
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
file = file.txt
directory = E:\dir\subdir

path = /dir/subdir/file.txt
file = file.txt
directory = \dir\subdir

path = .\dir\subdir\file.txt
file = file.txt
directory = .\dir\subdir

path = ./dir/subdir/file.txt
file = file.txt
directory = .\dir\subdir

path = file.txt
file = file.txt
directory =

実行結果
path = E:\dir\subdir\file.txt
file = E:\dir\subdir\file.txt
directory = 

path = /dir/subdir/file.txt
file = file.txt
directory = /dir/subdir

path = .\dir\subdir\file.txt
file = .\dir\subdir\file.txt
directory = 

path = ./dir/subdir/file.txt
file = file.txt
directory = ./dir/subdir

path = file.txt
file = file.txt
directory = 

GetFileName・GetDirectoryNameメソッドは、ファイル名・ディレクトリ名の抽出に際して、それが実際にファイルであるか、ディレクトリであるかを検証しません。 そのため、次の例のようにディレクトリ区切り文字で終わっていないパスは、仮にそれがディレクトリを表すものであってもファイル名として扱われる点に注意が必要です。 逆に言えば、ディレクトリ区切り文字で終わっていない限り、パスの最後の部分は必ずファイル名として扱われます。

Path.GetFileName/GetDirectoryNameメソッドではパスがディレクトリ区切り文字で終わるかどうかで結果が変わる
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"dir\subdir",  // ディレクトリを表すパス (ディレクトリ区切り文字で終わっていない場合・Windows形式)
      @"dir\subdir\", // ディレクトリを表すパス (ディレクトリ区切り文字で終わっている場合・Windows形式)
      @"dir/subdir",  // ディレクトリを表すパス (ディレクトリ区切り文字で終わっていない場合・UNIX形式)
      @"dir/subdir/", // ディレクトリを表すパス (ディレクトリ区切り文字で終わっている場合・UNIX形式)
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("file = {0}", Path.GetFileName(path));
      Console.WriteLine("directory = {0}", Path.GetDirectoryName(path));
      Console.WriteLine();
    }
  }
}
Path.GetFileName/GetDirectoryNameメソッドではパスがディレクトリ区切り文字で終わるかどうかで結果が変わる
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "dir\subdir",  ' ディレクトリを表すパス (ディレクトリ区切り文字で終わっていない場合・Windows形式)
      "dir\subdir\", ' ディレクトリを表すパス (ディレクトリ区切り文字で終わっている場合・Windows形式)
      "dir/subdir",  ' ディレクトリを表すパス (ディレクトリ区切り文字で終わっていない場合・UNIX形式)
      "dir/subdir/"  ' ディレクトリを表すパス (ディレクトリ区切り文字で終わっている場合・UNIX形式)
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("file = {0}", Path.GetFileName(p))
      Console.WriteLine("directory = {0}", Path.GetDirectoryName(p))
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = dir\subdir
file = subdir
directory = dir

path = dir\subdir\
file =
directory = dir\subdir

path = dir/subdir
file = subdir
directory = dir

path = dir/subdir/
file =
directory = dir\subdir

実行結果
path = dir\subdir
file = dir\subdir
directory = 

path = dir\subdir\
file = dir\subdir\
directory = 

path = dir/subdir
file = subdir
directory = dir

path = dir/subdir/
file = 
directory = dir/subdir

パスがディレクトリ区切り文字で終端されているかどうかを調べる、あるいはパス末尾のディレクトリ区切り文字を除去するには、EndsInDirectorySeparatorメソッド・TrimEndingDirectorySeparatorメソッドを使います。

拡張子を除いたファイル名 (GetFileNameWithoutExtension)

GetFileNameメソッドでパスからファイル名を抽出できるのに対して、GetFileNameWithoutExtensionメソッドではパスから拡張子を除いたファイル名を抽出します。 例えば、パスのファイル名部分がfile.txtの場合、GetFileNameWithoutExtensionメソッドはfileを返します。 パスから拡張子のみを抽出するには、GetExtensionメソッドを使います。

GetFileNameWithoutExtensionメソッドは、パスのファイル名部分に拡張子がなければ、ファイル名をそのまま返します。

GetFileNameWithoutExtensionメソッドでは、UNIX系OSで隠しファイルとして扱われるドットファイル(dot file)のようなピリオドで始まるファイル名に対して特別な考慮はされません。 そのため、ピリオドが一つだけ含まれるドットファイルは、ファイル名全体が拡張子と判断され、GetFileNameWithoutExtensionメソッドは空の文字列を返します。

Path.GetFileNameWithoutExtensionメソッドで拡張子を除いたファイル名を抽出する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @".\dir\subdir\file",       // 拡張子のないファイル (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"./dir/subdir/file",       // 拡張子のないファイル (UNIX形式)
      @"file.txt",                // ファイル名のみのパス
      @".file",                   // ピリオドで始まるファイル名
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("file = {0}", Path.GetFileName(path)); // パスからファイル名を抽出
      Console.WriteLine("file w/o ext = {0}", Path.GetFileNameWithoutExtension(path)); // パスから拡張子を除いたファイル名を抽出
      Console.WriteLine();
    }
  }
}
Path.GetFileNameWithoutExtensionメソッドで拡張子を除いたファイル名を抽出する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      ".\dir\subdir\file",      ' 拡張子のないファイル (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "./dir/subdir/file",      ' 拡張子のないファイル (UNIX形式)
      "file.txt",               ' ファイル名のみのパス
      ".file"                   ' ピリオドで始まるファイル名
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("file = {0}", Path.GetFileName(p)) ' パスからファイル名を抽出
      Console.WriteLine("file w/o ext = {0}", Path.GetFileNameWithoutExtension(p)) ' パスから拡張子を除いたファイル名を抽出
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
file = file.txt
file w/o ext = file

path = .\dir\subdir\file.txt
file = file.txt
file w/o ext = file

path = .\dir\subdir\file
file = file
file w/o ext = file

path = /dir/subdir/file.txt
file = file.txt
file w/o ext = file

path = ./dir/subdir/file.txt
file = file.txt
file w/o ext = file

path = ./dir/subdir/file
file = file
file w/o ext = file

path = file.txt
file = file.txt
file w/o ext = file

path = .file
file = .file
file w/o ext =

実行結果
path = E:\dir\subdir\file.txt
file = E:\dir\subdir\file.txt
file w/o ext = E:\dir\subdir\file

path = .\dir\subdir\file.txt
file = .\dir\subdir\file.txt
file w/o ext = .\dir\subdir\file

path = .\dir\subdir\file
file = .\dir\subdir\file
file w/o ext = 

path = /dir/subdir/file.txt
file = file.txt
file w/o ext = file

path = ./dir/subdir/file.txt
file = file.txt
file w/o ext = file

path = ./dir/subdir/file
file = file
file w/o ext = file

path = file.txt
file = file.txt
file w/o ext = file

path = .file
file = .file
file w/o ext = 

拡張子 (GetExtension)

パスまたはファイル名から拡張子を抽出するにはGetExtensionメソッドを使います。 GetExtensionメソッドでは、パスにファイル名がない場合でも例外にはならず、単に拡張子はないものとして空の文字列が返されます。 このメソッドでは、ピリオド.も含めて拡張子として扱われます。 (ファイル名がfile.txtなら拡張子は.txtとなる)

ファイル名が拡張子を持たない場合、拡張子がピリオドのみの場合は空の文字列が返されます。 ピリオドが複数あるファイル名の場合は、一番最後のピリオド以降を拡張子として返します。 (ファイル名がfile.txt.bakなら拡張子は.bakとなる)

GetExtensionメソッドでは、UNIX系OSで隠しファイルとして扱われるドットファイル(dot file)のようなピリオドで始まるファイル名に対して特別な考慮はされません。 そのため、ピリオドが一つだけ含まれるドットファイルは、ファイル名全体が拡張子と判断され、GetExtensionメソッドはファイル名をそのまま返します。

さらに非Windows環境では、ピリオドを含み、かつWindows形式のディレクトリ区切り記号\で終わるパスは、拡張子として判断される点に注意が必要です。 例えばパスsome.dir\はディレクトリではなくファイルを表すものと判断されるため、これに従い拡張子も.dir\であると判断されます。 パスsome.dir/では、ディレクトリを表すと判断されるため、これに従い拡張子はないものとして扱われます。

Path.GetExtensionメソッドで拡張子を抽出する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"file.txt",                // ファイル名のみのパス
      @"file",                    // 拡張子のないファイル名
      @"file.txt.bak",            // ピリオドを二つ以上含むファイル名
      @"file.",                   // ピリオドで終わるファイル名
      @"some.dir\",               // ピリオドを含むディレクトリ名のパス (Windows形式)
      @"some.dir/",               // ピリオドを含むディレクトリ名のパス (UNIX形式)
      @"dir/.file",               // ピリオドで始まるファイル名
      @".file",                   // ピリオドで始まるファイル名
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("ext = {0}", Path.GetExtension(path)); // パスからファイルの拡張子を抽出
      Console.WriteLine();
    }
  }
}
Path.GetExtensionメソッドで拡張子を抽出する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "file.txt",               ' ファイル名のみのパス
      "file",                   ' 拡張子のないファイル名
      "file.txt.bak",           ' ピリオドを二つ以上含むファイル名
      "file.",                  ' ピリオドで終わるファイル名
      "some.dir\",              ' ピリオドを含むディレクトリ名のパス (Windows形式)
      "some.dir/",              ' ピリオドを含むディレクトリ名のパス (UNIX形式)
      "dir/.file",              ' ピリオドで始まるファイル名
      ".file"                   ' ピリオドで始まるファイル名
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("ext = {0}", Path.GetExtension(p)) ' パスからファイルの拡張子を抽出
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
ext = .txt

path = /dir/subdir/file.txt
ext = .txt

path = .\dir\subdir\file.txt
ext = .txt

path = ./dir/subdir/file.txt
ext = .txt

path = file.txt
ext = .txt

path = file
ext =

path = file.txt.bak
ext = .bak

path = file.
ext =

path = some.dir\
ext =

path = some.dir/
ext =

path = dir/.file
ext = .file

path = .file
ext = .file

実行結果
path = E:\dir\subdir\file.txt
ext = .txt

path = /dir/subdir/file.txt
ext = .txt

path = .\dir\subdir\file.txt
ext = .txt

path = ./dir/subdir/file.txt
ext = .txt

path = file.txt
ext = .txt

path = file
ext = 

path = file.txt.bak
ext = .bak

path = file.
ext = 

path = some.dir\
ext = .dir\

path = some.dir/
ext = 

path = dir/.file
ext = .file

path = .file
ext = .file

パスが拡張子を持つかどうか (HasExtension)

HasExtensionメソッドを使うことで、パスの表すファイル部分が、拡張子を持つかどうかを調べることができます。 HasExtensionメソッドでは、パスにファイル名がない場合でも例外にはならず、単に拡張子がないものとしてFalseが返されます。

ファイル名部分のうち、どの部分を拡張子と判断するかの基準はGetExtensionメソッドと同様です。 このため、UNIX系OSで隠しファイルとして扱われるドットファイル(dot file)は、常に拡張子を持つものとして判断されます。

Path.HasExtensionメソッドで拡張子を持つファイル名かどうか調べる
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"file.txt",                // ファイル名のみのパス
      @"file",                    // 拡張子のないファイル名
      @"file.txt.bak",            // ピリオドを二つ以上含むファイル名
      @"file.",                   // ピリオドで終わるファイル名
      @"some.dir\",               // ピリオドを含むディレクトリ名のパス (Windows形式)
      @"some.dir/",               // ピリオドを含むディレクトリ名のパス (UNIX形式)
      @"dir/.file",               // ピリオドで始まるファイル名
      @".file",                   // ピリオドで始まるファイル名
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("has ext? {0}", Path.HasExtension(path)); // パスのファイル名が拡張子を含むかどうか
      Console.WriteLine();
    }
  }
}
Path.HasExtensionメソッドで拡張子を持つファイル名かどうか調べる
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "file.txt",               ' ファイル名のみのパス
      "file",                   ' 拡張子のないファイル名
      "file.txt.bak",           ' ピリオドを二つ以上含むファイル名
      "file.",                  ' ピリオドで終わるファイル名
      "some.dir\",              ' ピリオドを含むディレクトリ名のパス (Windows形式)
      "some.dir/",              ' ピリオドを含むディレクトリ名のパス (UNIX形式)
      "dir/.file",              ' ピリオドで始まるファイル名
      ".file"                   ' ピリオドで始まるファイル名
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("has ext? {0}", Path.HasExtension(p)) ' パスのファイル名が拡張子を含むかどうか
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
has ext? True

path = /dir/subdir/file.txt
has ext? True

path = .\dir\subdir\file.txt
has ext? True

path = ./dir/subdir/file.txt
has ext? True

path = file.txt
has ext? True

path = file
has ext? False

path = file.txt.bak
has ext? True

path = file.
has ext? False

path = some.dir\
has ext? False

path = some.dir/
has ext? False

path = dir/.file
has ext? True

path = .file
has ext? True

実行結果
path = E:\dir\subdir\file.txt
has ext? True

path = /dir/subdir/file.txt
has ext? True

path = .\dir\subdir\file.txt
has ext? True

path = ./dir/subdir/file.txt
has ext? True

path = file.txt
has ext? True

path = file
has ext? False

path = file.txt.bak
has ext? True

path = file.
has ext? False

path = some.dir\
has ext? True

path = some.dir/
has ext? False

path = dir/.file
has ext? True

path = .file
has ext? True

拡張子の置換・削除 (ChangeExtension)

パスまたはファイル名の拡張子を別の拡張子に置き換えるにはChangeExtensionメソッドを使います。 元のパス・ファイル名に拡張子がない場合は拡張子が追加され、ピリオドが二つ以上あるファイル名の場合は一番最後のピリオド以降が置き換えられます。

ファイル名部分のうち、どの部分を拡張子と判断するかの基準はGetExtensionメソッドと同様です。 このため、UNIX系OSで隠しファイルとして扱われるドットファイル(dot file)では、ピリオドが一つだけ含まれる場合はファイル名全体が拡張子と判断され、ファイル名に対する置換が行われることになります。 この他、拡張子に関する注意点についてはGetExtensionメソッドを参照してください。

Path.ChangeExtensionメソッドで拡張子を別の拡張子に置換する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"file.txt",                // ファイル名のみのパス
      @"file",                    // 拡張子のないファイル名
      @"file.txt.bak",            // ピリオドを二つ以上含むファイル名
      @"file.",                   // ピリオドで終わるファイル名
      @".file",                   // ピリオドで始まるファイル名
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("    => {0}", Path.ChangeExtension(path, ".dat")); // パスのファイル名の拡張子を.datに変更する
      Console.WriteLine();
    }

    // 変更後の拡張子はピリオドから始まっていても、いなくてもよい
    Console.WriteLine(Path.ChangeExtension("file.txt", "dat"));
    Console.WriteLine(Path.ChangeExtension("file.txt", ".dat"));

    // 変更後の拡張子は複数のピリオドを含んでいてもよい
    Console.WriteLine(Path.ChangeExtension("file.txt", "dat.bin"));
    Console.WriteLine(Path.ChangeExtension("file.txt", ".dat.bin"));
  }
}
Path.ChangeExtensionメソッドで拡張子を別の拡張子に置換する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "file.txt",               ' ファイル名のみのパス
      "file",                   ' 拡張子のないファイル
      "file.txt.bak",           ' ピリオドを二つ以上含むファイル名
      "file.",                  ' ピリオドで終わるファイル名
      ".file"                   ' ピリオドで始まるファイル名
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("    => {0}", Path.ChangeExtension(p, ".dat")) ' パスのファイル名の拡張子を.datに変更する
      Console.WriteLine()
    Next

    ' 変更後の拡張子はピリオドから始まっていても、いなくてもよい (どちらも.datに置き換えられる)
    Console.WriteLine(Path.ChangeExtension("file.txt", "dat"))
    Console.WriteLine(Path.ChangeExtension("file.txt", ".dat"))

    ' 変更後の拡張子は複数のピリオドを含んでいてもよい (どちらも.dat.binに置き換えられる)
    Console.WriteLine(Path.ChangeExtension("file.txt", "dat.bin"))
    Console.WriteLine(Path.ChangeExtension("file.txt", ".dat.bin"))
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
    => E:\dir\subdir\file.dat

path = /dir/subdir/file.txt
    => /dir/subdir/file.dat

path = .\dir\subdir\file.txt
    => .\dir\subdir\file.dat

path = ./dir/subdir/file.txt
    => ./dir/subdir/file.dat

path = file.txt
    => file.dat

path = file
    => file.dat

path = file.txt.bak
    => file.txt.dat

path = file.
    => file.dat

path = .file
    => .dat

file.dat
file.dat
file.dat.bin
file.dat.bin
実行結果
path = E:\dir\subdir\file.txt
    => E:\dir\subdir\file.dat

path = /dir/subdir/file.txt
    => /dir/subdir/file.dat

path = .\dir\subdir\file.txt
    => .\dir\subdir\file.dat

path = ./dir/subdir/file.txt
    => ./dir/subdir/file.dat

path = file.txt
    => file.dat

path = file
    => file.dat

path = file.txt.bak
    => file.txt.dat

path = file.
    => file.dat

path = .file
    => .dat

file.dat
file.dat
file.dat.bin
file.dat.bin

置き換える拡張子に空の文字列(String.Empty)を指定した場合、ピリオドだけの拡張子に置換されます。 一方、null/Nothingを指定した場合は、ピリオドも含めて拡張子が削除されます。

特に、ピリオドが一つだけ含まれるドットファイルの場合、ピリオドだけのファイル名.、あるいは長さ0のファイル名に変換されることになるため、注意が必要です。

Path.ChangeExtensionメソッドでファイル名から拡張子を削除する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"file.txt",                // ファイル名のみのパス
      @"file",                    // 拡張子のないファイル名
      @"file.txt.bak",            // ピリオドを二つ以上含むファイル名
      @"file.",                   // ピリオドで終わるファイル名
      @".file",                   // ピリオドで始まるファイル名
    }) {
      Console.WriteLine("path                 = {0}", path);

      // パスのファイル名から、ピリオドを残して拡張子を削除する
      Console.WriteLine("change ext (empty)  => {0}", Path.ChangeExtension(path, string.Empty));

      // パスのファイル名から、ピリオドも含めて拡張子を削除する
      Console.WriteLine("change ext (null)   => {0}", Path.ChangeExtension(path, null));

      Console.WriteLine();
    }
  }
}
Path.ChangeExtensionメソッドでファイル名から拡張子を削除する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "file.txt",               ' ファイル名のみのパス
      "file",                   ' 拡張子のないファイル
      "file.txt.bak",           ' ピリオドを二つ以上含むファイル名
      "file.",                  ' ピリオドで終わるファイル名
      ".file"                   ' ピリオドで始まるファイル名
    }
      Console.WriteLine("path                 = {0}", p)

      ' パスのファイル名から、ピリオドを残して拡張子を削除する
      Console.WriteLine("change ext (empty)  => {0}", Path.ChangeExtension(p, String.Empty))

      ' パスのファイル名から、ピリオドも含めて拡張子を削除する
      Console.WriteLine("change ext (null)   => {0}", Path.ChangeExtension(p, Nothing))

      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path                 = E:\dir\subdir\file.txt
change ext (empty)  => E:\dir\subdir\file.
change ext (null)   => E:\dir\subdir\file

path                 = /dir/subdir/file.txt
change ext (empty)  => /dir/subdir/file.
change ext (null)   => /dir/subdir/file

path                 = .\dir\subdir\file.txt
change ext (empty)  => .\dir\subdir\file.
change ext (null)   => .\dir\subdir\file

path                 = ./dir/subdir/file.txt
change ext (empty)  => ./dir/subdir/file.
change ext (null)   => ./dir/subdir/file

path                 = file.txt
change ext (empty)  => file.
change ext (null)   => file

path                 = file
change ext (empty)  => file.
change ext (null)   => file

path                 = file.txt.bak
change ext (empty)  => file.txt.
change ext (null)   => file.txt

path                 = file.
change ext (empty)  => file.
change ext (null)   => file

path                 = .file
change ext (empty)  => .
change ext (null)   =>

実行結果
path                 = E:\dir\subdir\file.txt
change ext (empty)  => E:\dir\subdir\file.
change ext (null)   => E:\dir\subdir\file

path                 = /dir/subdir/file.txt
change ext (empty)  => /dir/subdir/file.
change ext (null)   => /dir/subdir/file

path                 = .\dir\subdir\file.txt
change ext (empty)  => .\dir\subdir\file.
change ext (null)   => .\dir\subdir\file

path                 = ./dir/subdir/file.txt
change ext (empty)  => ./dir/subdir/file.
change ext (null)   => ./dir/subdir/file

path                 = file.txt
change ext (empty)  => file.
change ext (null)   => file

path                 = file
change ext (empty)  => file.
change ext (null)   => file

path                 = file.txt.bak
change ext (empty)  => file.txt.
change ext (null)   => file.txt

path                 = file.
change ext (empty)  => file.
change ext (null)   => file

path                 = .file
change ext (empty)  => .
change ext (null)   => 

ファイル名の置換

拡張子を置換するChangeExtensionメソッドが用意されている一方で、(少なくとも.NET 5時点までの)Pathクラスには拡張子を除くファイル名部分だけを置き換えるメソッド・ChangeFileNameのようなメソッドは用意されていません。 そのため、GetDirectoryNameメソッドGetExtensionメソッドなどを組み合わせて独自に実装する必要があります。

以下はパスのうち、拡張子を除くファイル名部分を置き換えるメソッドを実装した例です。

Pathクラスのメソッドを組み合わせてパスのファイル名部分だけを置換する
using System;
using System.IO;

class Sample {
  // パスの拡張子を除いたファイル名部分だけを置き換えるメソッド
  static string ChangeFileName(string path, string newFileName)
  {
    var dir = Path.GetDirectoryName(path); // ディレクトリ部分を抽出
    var ext = Path.GetExtension(path); // 拡張子部分を抽出

    // ディレクトリ、新しいファイル名、拡張子を連結して返す
    return Path.Combine(dir, newFileName + ext);
  }

  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",  // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",    // 絶対パス (UNIX形式)
      @".\dir\subdir\file.txt",   // 相対パス (Windows形式)
      @"./dir/subdir/file.txt",   // 相対パス (UNIX形式)
      @"file.txt",                // ファイル名のみのパス
      @"file",                    // 拡張子のないファイル名
      @"file.txt.bak",            // ピリオドを二つ以上含むファイル名
      @"file.",                   // ピリオドで終わるファイル名
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("    => {0}", ChangeFileName(path, "text")); // 拡張子を維持してパスのファイル名をtextに変える
      Console.WriteLine();
    }
  }
}
Pathクラスのメソッドを組み合わせてパスのファイル名部分だけを置換する
Imports System
Imports System.IO

Class Sample
  ' パスの拡張子を除いたファイル名部分だけを置き換えるメソッド
  Shared Function ChangeFileName(ByVal p As String, ByVal newFileName As String) As String
    Dim dir As String = Path.GetDirectoryName(p) ' ディレクトリ部分を抽出
    Dim ext As String = Path.GetExtension(p) ' 拡張子部分を抽出

    ' ディレクトリ、新しいファイル名、拡張子を連結して返す
    Return Path.Combine(dir, newFileName + ext)
  End Function

  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt", ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",   ' 絶対パス (UNIX形式)
      ".\dir\subdir\file.txt",  ' 相対パス (Windows形式)
      "./dir/subdir/file.txt",  ' 相対パス (UNIX形式)
      "file.txt",               ' ファイル名のみのパス
      "file",                   ' 拡張子のないファイル
      "file.txt.bak",           ' ピリオドを二つ以上含むファイル名
      "file."                   ' ピリオドで終わるファイル名
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("    => {0}", ChangeFileName(p, "text")) ' 拡張子を維持してパスのファイル名をtextに変える
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
    => E:\dir\subdir\text.txt

path = /dir/subdir/file.txt
    => \dir\subdir\text.txt

path = .\dir\subdir\file.txt
    => .\dir\subdir\text.txt

path = ./dir/subdir/file.txt
    => .\dir\subdir\text.txt

path = file.txt
    => text.txt

path = file
    => text

path = file.txt.bak
    => text.bak

path = file.
    => text

実行結果
path = E:\dir\subdir\file.txt
    => text.txt

path = /dir/subdir/file.txt
    => /dir/subdir/text.txt

path = .\dir\subdir\file.txt
    => text.txt

path = ./dir/subdir/file.txt
    => ./dir/subdir/text.txt

path = file.txt
    => text.txt

path = file
    => text

path = file.txt.bak
    => text.bak

path = file.
    => text

この例で使用しているPath.Combineメソッドについては§.パスの結合 (Combine)を参照してください。

ドライブ名(ルートディレクトリ) (GetPathRoot)

ドライブ名だけを返すメソッドは用意されていませんが、GetPathRootメソッドでパスからルートディレクトリを取得できるので、これを使うことでドライブ名を取得することができます。

ただし、完全修飾されているパスの場合はホスト名を含めたドライブ名までの部分が返されるため、単にドライブ名だけを取得することはできません。 また、カレントドライブからの相対パスの場合も、ドライブのルートディレクトリ\が返されるため、ドライブ名は取得できません。 ルートディレクトリを含まない相対パスの場合は、空の文字列が返されます。

Path.GetPathRootメソッドでドライブ名・ルートディレクトリを抽出する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",      // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",        // 絶対パス (UNIX形式)
      @"\\127.0.0.1\e$\dir\file.txt", // UNCパス
      @"\dir\subdir\file.txt",        // カレントドライブからの相対パス (Windows形式)
      @".\dir\subdir\file.txt",       // カレントディレクトリからの相対パス (Windows形式)
      @"./dir/subdir/file.txt",       // カレントディレクトリからの相対パス (UNIX形式)
      @"file.txt",                    // ファイル名のみのパス
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("root = {0}", Path.GetPathRoot(path)); // パスからドライブ名・ルートディレクトリを抽出する
      Console.WriteLine();
    }
  }
}
Path.GetPathRootメソッドでドライブ名・ルートディレクトリを抽出する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt",       ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",         ' 絶対パス (UNIX形式)
      "\\127.0.0.1\e$\dir\file.txt",  ' UNCパス
      "\dir\subdir\file.txt",         ' カレントドライブからの相対パス (Windows形式)
      ".\dir\subdir\file.txt",        ' カレントディレクトリからの相対パス (Windows形式)
      "./dir/subdir/file.txt",        ' カレントディレクトリからの相対パス (UNIX形式)
      "file.txt"                      ' ファイル名のみのパス
    }
      Console.WriteLine("path = {0}", p)
      Console.WriteLine("root = {0}", Path.GetPathRoot(p)) ' パスからドライブ名・ルートディレクトリを抽出する
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
root = E:\

path = /dir/subdir/file.txt
root = \

path = \\127.0.0.1\e$\dir\file.txt
root = \\127.0.0.1\e$

path = \dir\subdir\file.txt
root = \

path = .\dir\subdir\file.txt
root =

path = ./dir/subdir/file.txt
root =

path = file.txt
root =

実行結果
path = E:\dir\subdir\file.txt
root = 

path = /dir/subdir/file.txt
root = /

path = \\127.0.0.1\e$\dir\file.txt
root = 

path = \dir\subdir\file.txt
root = 

path = .\dir\subdir\file.txt
root = 

path = ./dir/subdir/file.txt
root = 

path = file.txt
root = 

GetPathRootメソッドの詳細な動作については§.ルートディレクトリの取得・ルートディレクトリからのパスかどうか (GetPathRoot/IsPathRooted)を参照してください。

パスの結合

ここではパス同士の結合、ディレクトリ区切り文字等について解説します。

パスの結合 (Combine)

ディレクトリ名とファイル名、親ディレクトリと子ディレクトリなど、個別の部分(断片)からなるパスを結合して一つのパスにするにはCombineメソッドを使います。

Combineメソッドでは、絶対パスと相対パスとの結合や、相対パス同士の結合を行うことができます。 引数に指定されたパスは実行環境のディレクトリ区切り文字にしたがって解釈され、結合時には必要に応じて区切り文字が自動的に補われます。 特に非Windows環境での\は、区切りではなくディレクトリ名・ファイル名の一部として解釈された上で結合される点に注意が必要です。 また、パス内に含まれる"."や".."は、正規化されずそのまま結合されます。 (パスの正規化を行うにはGetFullPathメソッドを使用します)

このメソッドでは、2つ以上の要素からなる任意個のパスを結合できます。 複数の文字列、あるいは文字列の配列として渡すことができる一方で、(少なくとも.NET 5までの時点では)IEnumerable<String>を直接渡すことはできないため、ToArrayメソッドで配列に変換する必要があります。

Path.Combineメソッドでパスを結合する
using System;
using System.Collections.Generic;
using System.IO;

class Sample {
  static void Main()
  {
    // Path.Combineメソッドでパスを結合する
    Console.WriteLine(Path.Combine(@"E:\dir\", "file.txt"));              // 絶対パスのディレクトリとファイル名を結合する
    Console.WriteLine(Path.Combine(@"E:\dir", "file.txt"));               // 区切り文字で終わっていない絶対パスとファイル名を結合する (結果は上と同じ)
    Console.WriteLine(Path.Combine(@"E:\dir\", "subdir", "file.txt"));    // 絶対パスのディレクトリと、サブディレクトリ名、ファイル名を結合する
    Console.WriteLine(Path.Combine(@"E:\dir\", @"subdir\file.txt"));      // 絶対パスのディレクトリと相対パスを結合する (結果は上と同じ)
    Console.WriteLine(Path.Combine(@"E:\dir\", @"..\file.txt"));          // ".."(親ディレクトリ)を含む相対パスを結合する (正規化はされない)
    Console.WriteLine(Path.Combine(@"dir\subdir", @".\file.txt"));        // "."(カレントディレクトリ)を含む相対パス同士を結合する (正規化はされない)
    Console.WriteLine(Path.Combine("/dir", "subdir", "file.txt"));        // UNIX形式のルートディレクトリ、ディレクトリ名、ファイル名を結合する
    Console.WriteLine(Path.Combine("/dir", "../subdir", "./file.txt"));   // UNIX形式の相対パスを結合する
    Console.WriteLine();

    // Listに格納されたパスの断片
    var pathFragments = new List<string>() {"path", "to", "some", "file", "or", "directory"};

    // string[]を指定することはできるが、IEnumerable<string>は指定できないので
    // Listなどを指定したい場合は、いったん配列に変換する必要がある
    //Console.WriteLine(Path.Combine(pathFragments)); // error CS1503: 引数 1: は 'System.Collections.Generic.List<string>' から 'string' へ変換することはできません。
    Console.WriteLine(Path.Combine(pathFragments.ToArray()));
  }
}
Path.Combineメソッドでパスを結合する
Imports System
Imports System.Collections.Generic
Imports System.IO

Class Sample
  Shared Sub Main()
    ' Path.Combineメソッドでパスを結合する
    Console.WriteLine(Path.Combine("E:\dir\", "file.txt"))              ' 絶対パスのディレクトリとファイル名を結合する
    Console.WriteLine(Path.Combine("E:\dir", "file.txt"))               ' 区切り文字で終わっていない絶対パスとファイル名を結合する (結果は上と同じ)
    Console.WriteLine(Path.Combine("E:\dir\", "subdir", "file.txt"))    ' 絶対パスのディレクトリと、サブディレクトリ名、ファイル名を結合する
    Console.WriteLine(Path.Combine("E:\dir\", "subdir\file.txt"))       ' 絶対パスのディレクトリと相対パスを結合する (結果は上と同じ)
    Console.WriteLine(Path.Combine("E:\dir\", "..\file.txt"))           ' ".."(親ディレクトリ)を含む相対パスを結合する (正規化はされない)
    Console.WriteLine(Path.Combine("dir\subdir", ".\file.txt"))         ' "."(カレントディレクトリ)を含む相対パス同士を結合する (正規化はされない)
    Console.WriteLine(Path.Combine("/dir", "subdir", "file.txt"))       ' UNIX形式のルートディレクトリ、ディレクトリ名、ファイル名を結合する
    Console.WriteLine(Path.Combine("/dir", "../subdir", "./file.txt"))  ' UNIX形式の相対パスを結合する
    Console.WriteLine()

    ' Listに格納されたパスの断片
    Dim pathFragments As New List(Of String)() From {"path", "to", "some", "file", "or", "directory"}

    ' String()を指定することはできるが、IEnumerable(Of String)は指定できないので
    ' Listなどを指定したい場合は、いったん配列に変換する必要がある
    'Console.WriteLine(Path.Combine(pathFragments)) ' error BC30311: 型 'List(Of String)' の値を 'String' に変換できません。
    Console.WriteLine(Path.Combine(pathFragments.ToArray()))
  End Sub
End Class
実行結果
E:\dir\file.txt
E:\dir\file.txt
E:\dir\subdir\file.txt
E:\dir\subdir\file.txt
E:\dir\..\file.txt
dir\subdir\.\file.txt
/dir\subdir\file.txt
/dir\../subdir\./file.txt

path\to\some\file\or\directory
実行結果
E:\dir\/file.txt
E:\dir/file.txt
E:\dir\/subdir/file.txt
E:\dir\/subdir\file.txt
E:\dir\/..\file.txt
dir\subdir/.\file.txt
/dir/subdir/file.txt
/dir/../subdir/./file.txt

path/to/some/file/or/directory

Combineメソッドでは、引数に対してパスとして正しい形式となるような選択が行われます。 具体的には、引数リストにルートディレクトリからのパス(Path.IsPathRootedtrueとなるパス)が指定されている場合、そのうちの一番最後をルートディレクトリとして選択した上でパスの結合を行います。 それより前のパスは結合に際して用いられず、無視されます。

Path.Combineメソッドでは、一番最後のルートディレクトリからのパスとして結合される
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // Path.Combineメソッドでは、結合結果がルートディレクトリからのパスとして正しい形式となるよう、
    // 引数リスト内における一番最後のルートディレクトリからのパスが選択され、それより前のパスは無視される
    Console.WriteLine(Path.Combine(@"E:\", @"C:\"));                      // (Windowsの場合)"C:\"がルートディレクトリとして選択される
    Console.WriteLine(Path.Combine(@"E:\", @"C:\", "file.txt"));          // (Windowsの場合)"C:\"がルートディレクトリとして選択された上で、以降の引数との連結が行われる
    Console.WriteLine(Path.Combine(@"dir\", @"C:\", "file.txt"));         // (Windowsの場合)同上、最初の引数は無視される
    Console.WriteLine(Path.Combine(@"E:\", @"\dir\file.txt"));            // (Windowsの場合)カレントドライブの"\"がルートディレクトリとして選択されるため、結果として"\dir\file.txt"が返される
    Console.WriteLine();

    Console.WriteLine(Path.Combine("/root", "/dir"));                     // (非Windowsの場合)"/dir"がルートディレクトリとして選択される
    Console.WriteLine(Path.Combine("/root", "/dir", "file.txt"));         // (非Windowsの場合)"/dir"がルートディレクトリとして選択された上で、以降の引数との連結が行われる
    Console.WriteLine(Path.Combine("dir/", "/dir", "file.txt"));          // (非Windowsの場合)同上、最初の引数は無視される
  }
}
Path.Combineメソッドでは、一番最後のルートディレクトリからのパスとして結合される
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' Path.Combineメソッドでは、結合結果がルートディレクトリからのパスとして正しい形式となるよう、
    ' 引数リスト内における一番最後のルートディレクトリからのパスが選択され、それより前のパスは無視される
    Console.WriteLine(Path.Combine("E:\", "C:\"))               ' (Windowsの場合)"C:\"がルートディレクトリとして選択される
    Console.WriteLine(Path.Combine("E:\", "C:\", "file.txt"))   ' (Windowsの場合)"C:\"がルートディレクトリとして選択された上で、以降の引数との連結が行われる
    Console.WriteLine(Path.Combine("dir\", "C:\", "file.txt"))  ' (Windowsの場合)同上、最初の引数は無視される
    Console.WriteLine(Path.Combine("E:\", "\dir\file.txt"))     ' (Windowsの場合)カレントドライブの"\"がルートディレクトリとして選択されるため、結果として"\dir\file.txt"が返される
    Console.WriteLine()

    Console.WriteLine(Path.Combine("/root", "/dir"))              ' (非Windowsの場合)"/dir"がルートディレクトリとして選択される
    Console.WriteLine(Path.Combine("/root", "/dir", "file.txt"))  ' (非Windowsの場合)"/dir"がルートディレクトリとして選択された上で、以降の引数との連結が行われる
    Console.WriteLine(Path.Combine("dir/", "/dir", "file.txt"))   ' (非Windowsの場合)同上、最初の引数は無視される
  End Sub
End Class
実行結果
C:\
C:\file.txt
C:\file.txt
\dir\file.txt

/dir
/dir\file.txt
/dir\file.txt
実行結果
E:\/C:\
E:\/C:\/file.txt
dir\/C:\/file.txt
E:\/\dir\file.txt

/dir
/dir/file.txt
/dir/file.txt

ルートディレクトリからのパスかどうかの解釈は行わず、単にパス同士を連結させたい場合はPath.Joinメソッドを使用します。

階層構造を無視したパスの結合 (Join)

ルートディレクトリからのパスかどうかによって動作が変わるCombineメソッドとは異なり、Joinメソッドはパスの階層構造の解釈は行わず、単にパスの断片として結合を行います。 必要に応じてディレクトリ区切り文字が自動的に補われる点はCombineメソッドと同様です。

Joinメソッドでは結合された結果がパスとして正しい形式になるとは限らないため、常に正しい形式のパスとして結合したい場合はCombineメソッドを使用します。

Path.Joinメソッドでパスの断片を結合する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // Path.Joinメソッドでパスの断片を結合する (比較として、Path.Combineメソッドでの結合も行う)

    // Windows形式のパス
    Console.WriteLine("Join:    {0}", Path.Join   (@"E:\", @"\dir", "file.txt")); // それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine(@"E:\", @"\dir", "file.txt")); // "\dir"からのパスとして結合される
    Console.WriteLine();

    Console.WriteLine("Join:    {0}", Path.Join   (@"E:\", @"\dir", @"\subdir\", "file.txt")); // それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine(@"E:\", @"\dir", @"\subdir\", "file.txt")); // "\subdir"からのパスとして結合される
    Console.WriteLine();

    Console.WriteLine("Join:    {0}", Path.Join   ("E:", "C:", "subdir", "file.txt")); // それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("E:", "C:", "subdir", "file.txt")); // "C:\"からのパスとして結合される
    Console.WriteLine();

    // UNIX形式のパス
    Console.WriteLine("Join:    {0}", Path.Join   ("/root", "/dir", "file.txt")); // それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("/root", "/dir", "file.txt")); // "/dir"からのパスとして結合される
    Console.WriteLine();

    Console.WriteLine("Join:    {0}", Path.Join   ("/root", "/dir", "/subdir/", "file.txt")); // それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("/root", "/dir", "/subdir/", "file.txt")); // "/subdir"からのパスとして結合される
    Console.WriteLine();
  }
}
Path.Joinメソッドでパスの断片を結合する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' Path.Joinメソッドでパスの断片を結合する (比較として、Path.Combineメソッドでの結合も行う)

    ' Windows形式のパス
    Console.WriteLine("Join:    {0}", Path.Join   ("E:\", "\dir", "file.txt")) ' それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("E:\", "\dir", "file.txt")) ' "\dir"からのパスとして結合される
    Console.WriteLine()

    Console.WriteLine("Join:    {0}", Path.Join   ("E:\", "\dir", "\subdir\", "file.txt")) ' それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("E:\", "\dir", "\subdir\", "file.txt")) ' "\subdir"からのパスとして結合される
    Console.WriteLine()

    Console.WriteLine("Join:    {0}", Path.Join   ("E:", "C:", "subdir", "file.txt")) ' それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("E:", "C:", "subdir", "file.txt")) ' "C:\"からのパスとして結合される
    Console.WriteLine()

    ' UNIX形式のパス
    Console.WriteLine("Join:    {0}", Path.Join   ("/root", "/dir", "file.txt")) ' それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("/root", "/dir", "file.txt")) ' "/dir"からのパスとして結合される
    Console.WriteLine()

    Console.WriteLine("Join:    {0}", Path.Join   ("/root", "/dir", "/subdir/", "file.txt")) ' それぞれ単にパスの一部として結合される
    Console.WriteLine("Combine: {0}", Path.Combine("/root", "/dir", "/subdir/", "file.txt")) ' "/subdir"からのパスとして結合される
    Console.WriteLine()
  End Sub
End Class
実行結果
Join:    E:\\dir\file.txt
Combine: \dir\file.txt

Join:    E:\\dir\subdir\file.txt
Combine: \subdir\file.txt

Join:    E:\C:\subdir\file.txt
Combine: C:\subdir\file.txt

Join:    /root/dir\file.txt
Combine: /dir\file.txt

Join:    /root/dir/subdir/file.txt
Combine: /subdir/file.txt

実行結果
Join:    E:\/\dir/file.txt
Combine: E:\/\dir/file.txt

Join:    E:\/\dir/\subdir\/file.txt
Combine: E:\/\dir/\subdir\/file.txt

Join:    E:/C:/subdir/file.txt
Combine: E:/C:/subdir/file.txt

Join:    /root/dir/file.txt
Combine: /dir/file.txt

Join:    /root/dir/subdir/file.txt
Combine: /subdir/file.txt

ディレクトリ区切り文字 (DirectorySeparatorChar/AltDirectorySeparatorChar)

現在の実行環境におけるディレクトリ区切り文字を知りたい場合はDirectorySeparatorCharフィールドを参照します。 Pathクラスでは自動的に適切なディレクトリ区切り文字が使用されます。 例えば、Windows上では\、非Windows環境では/がディレクトリ区切り文字となります。

AltDirectorySeparatorCharフィールドからは、代替となるディレクトリ区切り文字を取得できます。 Windows上では、\だけでなく/をディレクトリ区切り文字として用いたパスもサポートされていて、この文字が代替ディレクトリ区切り文字として定義されています。 逆に、非Windows環境では/のみがサポートされます。

なお、代替文字/を含むパスをPathクラスで処理した場合、ディレクトリ区切り文字が\に置き換えられる場合があります。

Path.DirectorySeparatorChar/AltDirectorySeparatorCharフィールドを参照して現在の環境でのディレクトリ区切り文字を取得する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ディレクトリ区切り文字を取得
    Console.WriteLine($"{nameof(Path.DirectorySeparatorChar)} = {Path.DirectorySeparatorChar}");

    // ディレクトリ区切り文字の代替文字を取得
    Console.WriteLine($"{nameof(Path.AltDirectorySeparatorChar)} = {Path.AltDirectorySeparatorChar}");

    Console.WriteLine();

    // ディレクトリ名とファイル名の結合 (上記のディレクトリ区切り文字が用いられる)
    Console.WriteLine(Path.Combine("dir", "file.txt"));
    Console.WriteLine();

    // ディレクトリ名とファイル名の抽出 (上記のディレクトリ区切り文字・代替文字に基づいて抽出される)
    var path1 = @"dir\subdir\file.txt";
    var path2 =  "dir/subdir/file.txt";

    Console.WriteLine("<{0}> <{1}>", Path.GetDirectoryName(path1), Path.GetFileName(path1));
    Console.WriteLine("<{0}> <{1}>", Path.GetDirectoryName(path2), Path.GetFileName(path2));
  }
}
Path.DirectorySeparatorChar/AltDirectorySeparatorCharフィールドを参照して現在の環境でのディレクトリ区切り文字を取得する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' ディレクトリ区切り文字を取得
    Console.WriteLine($"{NameOf(Path.DirectorySeparatorChar)} = {Path.DirectorySeparatorChar}")

    ' ディレクトリ区切り文字の代替文字を取得
    Console.WriteLine($"{NameOf(Path.AltDirectorySeparatorChar)} = {Path.AltDirectorySeparatorChar}")

    Console.WriteLine()

    ' ディレクトリ名とファイル名の結合 (上記のディレクトリ区切り文字が用いられる)
    Console.WriteLine(Path.Combine("dir", "file.txt"))
    Console.WriteLine()

    ' ディレクトリ名とファイル名の抽出 (上記のディレクトリ区切り文字・代替文字に基づいて抽出される)
    Dim path1 As String = "dir\subdir\file.txt"
    Dim path2 As String = "dir/subdir/file.txt"

    Console.WriteLine("<{0}> <{1}>", Path.GetDirectoryName(path1), Path.GetFileName(path1))
    Console.WriteLine("<{0}> <{1}>", Path.GetDirectoryName(path2), Path.GetFileName(path2))
  End Sub
End Class
実行結果
DirectorySeparatorChar = \
AltDirectorySeparatorChar = /

dir\file.txt

<dir\subdir> <file.txt>
<dir\subdir> <file.txt>
実行結果
DirectorySeparatorChar = /
AltDirectorySeparatorChar = /

dir/file.txt

<> <dir\subdir\file.txt>
<dir/subdir> <file.txt>

Path.DirectorySeparatorCharフィールドの値を見ることで現在の実行環境をある程度判別することもできますが、より厳密に判別する方法は別途用意されています。 詳しくはランタイム・システム・プラットフォームの情報 §.OSバージョンを参照してください。

パス区切り文字 (PathSeparator)

PathSeparatorフィールドを参照することで、現在の実行環境でのパス区切り文字を取得することができます。

ディレクトリ区切り文字(DirectorySeparatorChar)は単一のパス内におけるディレクトリ階層の区切りとして用いられる文字であるのに対して、パス区切り文字(PathSeparator)は複数パスを1行で記述する際の各パスの区切りとして用いられる文字を表します。 例えば環境変数PATHで複数のパスを設定したい場合には、パスひとつひとつの間をこの文字で区切った上で連結します。 Windowsではパス区切り文字に;、UNIX系では:が用いられます。

Pathクラスでは複数パスを連結・分割するようなメソッドは直接提供されませんが、環境変数を扱う場合などにはこのフィールドから適切な区切り文字を取得することができます。

Path.PathSeparatorフィールドを参照して現在の環境でのパス区切り文字を取得する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // パス区切り文字を取得
    Console.WriteLine($"{nameof(Path.PathSeparator)} = {Path.PathSeparator}");

    // 複数のパスを結合する
    Console.WriteLine(string.Join(Path.PathSeparator, new[] { @"C:\Windows\", @"C:\Program Files\", "/usr/bin", "/sbin" }));
  }
}
Path.PathSeparatorフィールドを参照して現在の環境でのパス区切り文字を取得する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' パス区切り文字を取得
    Console.WriteLine($"{NameOf(Path.PathSeparator)} = {Path.PathSeparator}")

    ' 複数のパスを結合する
    Console.WriteLine(String.Join(Path.PathSeparator, New String() { "C:\Windows\", "C:\Program Files\", "/usr/bin", "/sbin" }))
  End Sub
End Class
実行結果
PathSeparator = ;
C:\Windows\;C:\Program Files\;/usr/bin;/sbin
実行結果
PathSeparator = :
C:\Windows\:C:\Program Files\:/usr/bin:/sbin
Path.PathSeparatorフィールドの値に基づいて環境変数PATHに設定されているパスを分割する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // 環境変数PATHに設定されている値を取得する
    var envvar_path = Environment.GetEnvironmentVariable("PATH");

    Console.WriteLine($"PATH={envvar_path}");
    Console.WriteLine();

    // 設定されているパスを分割して表示する
    foreach (var path in envvar_path.Split(Path.PathSeparator)) {
      Console.WriteLine(path);
    }
  }
}
Path.PathSeparatorフィールドの値に基づいて環境変数PATHに設定されているパスを分割する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' 環境変数PATHに設定されている値を取得する
    Dim envvar_path As String = Environment.GetEnvironmentVariable("PATH")

    Console.WriteLine($"PATH={envvar_path}")
    Console.WriteLine()

    ' 設定されているパスを分割して表示する
    For Each p As String In envvar_path.Split(Path.PathSeparator)
      Console.WriteLine(p)
    Next
  End Sub
End Class
実行結果例
PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft\Web Platform Installer\;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\dotnet\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\TortoiseSVN\bin;C:\Program Files\TortoiseGit\bin;C:\Users\smdn\AppData\Local\Microsoft\WindowsApps;C:\Users\smdn\.dotnet\tools;C:\Users\smdn\AppData\Local\Microsoft\WindowsApps;C:\Users\smdn\.dotnet\tools;C:\Users\smdn\AppData\Local\Programs\Microsoft VS Code\bin

C:\WINDOWS\system32
C:\WINDOWS
C:\WINDOWS\System32\Wbem
C:\WINDOWS\System32\WindowsPowerShell\v1.0\
C:\Program Files\Microsoft\Web Platform Installer\
C:\Program Files\Microsoft SQL Server\130\Tools\Binn\
C:\Program Files\dotnet\
C:\WINDOWS\System32\OpenSSH\
C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\
C:\Program Files\TortoiseSVN\bin
C:\Program Files\TortoiseGit\bin
C:\Users\smdn\AppData\Local\Microsoft\WindowsApps
C:\Users\smdn\.dotnet\tools
C:\Users\smdn\AppData\Local\Microsoft\WindowsApps
C:\Users\smdn\.dotnet\tools
C:\Users\smdn\AppData\Local\Programs\Microsoft VS Code\bin
実行結果例
PATH=/home/smdn/.local/bin:/home/smdn/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/smdn/.dotnet/tools

/home/smdn/.local/bin
/home/smdn/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
/home/smdn/.dotnet/tools

環境変数を取得・設定する方法などについては、環境変数を参照してください。

パス形式の変換・検証・正規化

ここでは絶対パス・相対パス形式の変換や正規化、パス形式の検証について解説します。

絶対パスの取得・パスの正規化 (GetFullPath)

正規化された絶対パスを取得するにはGetFullPathメソッドを使います。 このメソッドを呼び出すと、相対パスは解決された状態になり、正規化された絶対パス・ルートディレクトリを基準(あるいは基点、base)とするパスが返されます。 カレントドライブからの相対パス(\で始まるパス)の場合も、ドライブ名を含む絶対パスに正規化されます。

.NET Standard 2.1/.NET Core 2.1以降では引数basePathを指定することができ、このパスを基準として相対パスが解決されます。 basePathには実在しないパスを指定することもできます。

また、basePathは常にディレクトリを表すものとして扱われるため、basePathがディレクトリ区切り文字で終端されているかどうかによって結果が変わることはありません。 当然、basePathに相対パスを指定することはできません

Path.GetFullPathメソッドで特定のディレクトリを基準として絶対パスを取得・正規化する .NET Standard 2.1/.NET Core 2.1
using System;
using System.IO;
using System.Runtime.InteropServices;

class Sample {
  static void Main()
  {
    // 基準ディレクトリ(相対パスを絶対パスに変換する際の基準となるディレクトリ)
    var baseDir = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
      ? @"C:\Users\user\"
      : "/home/user/";

    Console.WriteLine("base directory: {0}", baseDir);
    Console.WriteLine();

    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",    // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",      // 絶対パス (UNIX形式)
      @"E:\dir\subdir\.\file.txt",  // カレントディレクトリからの相対パスを含むパス (Windows形式)
      @"/dir/subdir/.\file.txt",    // カレントディレクトリからの相対パスを含むパス (UNIX形式)
      @"E:\dir\subdir\..\file.txt", // 親ディレクトリからの相対パスを含むパス (Windows形式)
      @"/dir/subdir/..\file.txt",   // 親ディレクトリからの相対パスを含むパス (UNIX形式)
      @"\dir\subdir\file.txt",      // カレントドライブからの相対パス (Windows形式)
      @".\file.txt",                // カレントディレクトリからの相対パス (Windows形式)
      @"./file.txt",                // カレントディレクトリからの相対パス (UNIX形式)
      @"..\file.txt",               // 親ディレクトリからの相対パス (Windows形式)
      @"../file.txt",               // 親ディレクトリからの相対パス (UNIX形式)
      @"file.txt",                  // ファイル名のみのパス
    }) {
      Console.WriteLine("path      = {0}", path);
      Console.WriteLine("full path = {0}", Path.GetFullPath(path, baseDir)); // baseDirを基準に正規化した絶対パスを取得する
      Console.WriteLine();
    }
  }
}
Path.GetFullPathメソッドで特定のディレクトリを基準として絶対パスを取得・正規化する .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.IO
Imports System.Runtime.InteropServices

Class Sample
  Shared Sub Main()
    ' 基準ディレクトリ(相対パスを絶対パスに変換する際の基準となるディレクトリ)
    Dim baseDir As String = If(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "C:\Users\user\", "/home/user/")

    Console.WriteLine("base directory: {0}", baseDir)
    Console.WriteLine()

    For Each p As String In New String() {
      "E:\dir\subdir\file.txt",     ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",       ' 絶対パス (UNIX形式)
      "E:\dir\subdir\.\file.txt",   ' カレントディレクトリからの相対パスを含むパス (Windows形式)
      "/dir/subdir/.\file.txt",     ' カレントディレクトリからの相対パスを含むパス (UNIX形式)
      "E:\dir\subdir\..\file.txt",  ' 親ディレクトリからの相対パスを含むパス (Windows形式)
      "/dir/subdir/..\file.txt",    ' 親ディレクトリからの相対パスを含むパス (UNIX形式)
      "\dir\subdir\file.txt",       ' カレントドライブからの相対パス (Windows形式)
      ".\file.txt",                 ' カレントディレクトリからの相対パス (Windows形式)
      "./file.txt",                 ' カレントディレクトリからの相対パス (UNIX形式)
      "..\file.txt",                ' 親ディレクトリからの相対パス (Windows形式)
      "../file.txt",                ' 親ディレクトリからの相対パス (UNIX形式)
      "file.txt"                    ' ファイル名のみのパス
    }
      Console.WriteLine("path      = {0}", p)
      Console.WriteLine("full path = {0}", Path.GetFullPath(p, baseDir))  ' baseDirを基準に正規化した絶対パスを取得する
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
base directory: C:\Users\user\

path      = E:\dir\subdir\file.txt
full path = E:\dir\subdir\file.txt

path      = /dir/subdir/file.txt
full path = C:\dir\subdir\file.txt

path      = E:\dir\subdir\.\file.txt
full path = E:\dir\subdir\file.txt

path      = /dir/subdir/.\file.txt
full path = C:\dir\subdir\file.txt

path      = E:\dir\subdir\..\file.txt
full path = E:\dir\file.txt

path      = /dir/subdir/..\file.txt
full path = C:\dir\file.txt

path      = \dir\subdir\file.txt
full path = C:\dir\subdir\file.txt

path      = .\file.txt
full path = C:\Users\user\file.txt

path      = ./file.txt
full path = C:\Users\user\file.txt

path      = ..\file.txt
full path = C:\Users\file.txt

path      = ../file.txt
full path = C:\Users\file.txt

path      = file.txt
full path = C:\Users\user\file.txt

実行結果
base directory: /home/user/

path      = E:\dir\subdir\file.txt
full path = /home/user/E:\dir\subdir\file.txt

path      = /dir/subdir/file.txt
full path = /dir/subdir/file.txt

path      = E:\dir\subdir\.\file.txt
full path = /home/user/E:\dir\subdir\.\file.txt

path      = /dir/subdir/.\file.txt
full path = /dir/subdir/.\file.txt

path      = E:\dir\subdir\..\file.txt
full path = /home/user/E:\dir\subdir\..\file.txt

path      = /dir/subdir/..\file.txt
full path = /dir/subdir/..\file.txt

path      = \dir\subdir\file.txt
full path = /home/user/\dir\subdir\file.txt

path      = .\file.txt
full path = /home/user/.\file.txt

path      = ./file.txt
full path = /home/user/file.txt

path      = ..\file.txt
full path = /home/user/..\file.txt

path      = ../file.txt
full path = /home/file.txt

path      = file.txt
full path = /home/user/file.txt

引数basePathを指定しないバージョンのGetFullPathメソッドを呼び出した場合、相対パスは常にカレントディレクトリを基準として解決されます。

Path.GetFullPathメソッドでカレントディレクトリを基準として絶対パスを取得・正規化する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // カレントディレクトリを表示(Path.GetFullPathメソッドでの相対パスの解決に使用される)
    Console.WriteLine("current directory: {0}", Environment.CurrentDirectory);
    Console.WriteLine();

    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",    // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",      // 絶対パス (UNIX形式)
      @"E:\dir\subdir\.\file.txt",  // カレントディレクトリからの相対パスを含むパス (Windows形式)
      @"/dir/subdir/.\file.txt",    // カレントディレクトリからの相対パスを含むパス (UNIX形式)
      @"E:\dir\subdir\..\file.txt", // 親ディレクトリからの相対パスを含むパス (Windows形式)
      @"/dir/subdir/..\file.txt",   // 親ディレクトリからの相対パスを含むパス (UNIX形式)
      @"\dir\subdir\file.txt",      // カレントドライブからの相対パス (Windows形式)
      @".\file.txt",                // カレントディレクトリからの相対パス (Windows形式)
      @"./file.txt",                // カレントディレクトリからの相対パス (UNIX形式)
      @"..\file.txt",               // 親ディレクトリからの相対パス (Windows形式)
      @"../file.txt",               // 親ディレクトリからの相対パス (UNIX形式)
      @"file.txt",                  // ファイル名のみのパス
    }) {
      Console.WriteLine("path      = {0}", path);
      Console.WriteLine("full path = {0}", Path.GetFullPath(path)); // 正規化した絶対パスを取得する
      Console.WriteLine();
    }
  }
}
Path.GetFullPathメソッドでカレントディレクトリを基準として絶対パスを取得・正規化する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' カレントディレクトリを表示(Path.GetFullPathメソッドでの相対パスの解決に使用される)
    Console.WriteLine("current directory: {0}", Environment.CurrentDirectory)
    Console.WriteLine()

    For Each p As String In New String() {
      "E:\dir\subdir\file.txt",     ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",       ' 絶対パス (UNIX形式)
      "E:\dir\subdir\.\file.txt",   ' カレントディレクトリからの相対パスを含むパス (Windows形式)
      "/dir/subdir/.\file.txt",     ' カレントディレクトリからの相対パスを含むパス (UNIX形式)
      "E:\dir\subdir\..\file.txt",  ' 親ディレクトリからの相対パスを含むパス (Windows形式)
      "/dir/subdir/..\file.txt",    ' 親ディレクトリからの相対パスを含むパス (UNIX形式)
      "\dir\subdir\file.txt",       ' カレントドライブからの相対パス (Windows形式)
      ".\file.txt",                 ' カレントディレクトリからの相対パス (Windows形式)
      "./file.txt",                 ' カレントディレクトリからの相対パス (UNIX形式)
      "..\file.txt",                ' 親ディレクトリからの相対パス (Windows形式)
      "../file.txt",                ' 親ディレクトリからの相対パス (UNIX形式)
      "file.txt"                    ' ファイル名のみのパス
    }
      Console.WriteLine("path      = {0}", p)
      Console.WriteLine("full path = {0}", Path.GetFullPath(p)) ' 正規化した絶対パスを取得する
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
current directory: C:\Users\user

path      = E:\dir\subdir\file.txt
full path = E:\dir\subdir\file.txt

path      = /dir/subdir/file.txt
full path = C:\dir\subdir\file.txt

path      = E:\dir\subdir\.\file.txt
full path = E:\dir\subdir\file.txt

path      = /dir/subdir/.\file.txt
full path = C:\dir\subdir\file.txt

path      = E:\dir\subdir\..\file.txt
full path = E:\dir\file.txt

path      = /dir/subdir/..\file.txt
full path = C:\dir\file.txt

path      = \dir\subdir\file.txt
full path = C:\dir\subdir\file.txt

path      = .\file.txt
full path = C:\Users\user\file.txt

path      = ./file.txt
full path = C:\Users\user\file.txt

path      = ..\file.txt
full path = C:\Users\file.txt

path      = ../file.txt
full path = C:\Users\file.txt

path      = file.txt
full path = C:\Users\user\file.txt

実行結果
current directory: /home/user

path      = E:\dir\subdir\file.txt
full path = /home/user/E:\dir\subdir\file.txt

path      = /dir/subdir/file.txt
full path = /dir/subdir/file.txt

path      = E:\dir\subdir\.\file.txt
full path = /home/user/E:\dir\subdir\.\file.txt

path      = /dir/subdir/.\file.txt
full path = /dir/subdir/.\file.txt

path      = E:\dir\subdir\..\file.txt
full path = /home/user/E:\dir\subdir\..\file.txt

path      = /dir/subdir/..\file.txt
full path = /dir/subdir/..\file.txt

path      = \dir\subdir\file.txt
full path = /home/user/\dir\subdir\file.txt

path      = .\file.txt
full path = /home/user/.\file.txt

path      = ./file.txt
full path = /home/user/file.txt

path      = ..\file.txt
full path = /home/user/..\file.txt

path      = ../file.txt
full path = /home/file.txt

path      = file.txt
full path = /home/user/file.txt

Path.GetRelativePathメソッドを使うことで相対パスを取得することができます。

カレントディレクトリの取得・設定についてはプロセス・アセンブリの情報 §.カレントディレクトリを参照してください。

相対パスの取得 (GetRelativePath)

Path.GetRelativePathメソッドを使用することで、目的のパスと基準(あるいは基点、base)となるパスの2つの絶対パスから相対パスを取得することができます。

基準となるパスを表す引数relativeToは、常にディレクトリを表すものとして扱われるため、relativeToがディレクトリ区切り文字で終端されているかどうかによって結果が変わることはありません。 異なるドライブ間のパス同士の場合は、絶対パスとして結果が返されます。 (F:\fromT:\toの場合はT:\to) 同一ドライブ内のパス同士の場合でも、カレントドライブからの相対パス形式とはならず、ディレクトリ間の相対パスとして結果が返されます。 (E:\fromE:\toの場合は\toではなく..\to) 

なお、GetRelativePathメソッドは.NET Standard 2.1/.NET Core 2.0以降で使用できます。 GetRelativePathメソッドが使用できない場合は、Uriクラスを使ってURIとして処理することにより相対パスを得られます。

Path.GetRelativePathメソッドでパス間の相対パスを取得する  .NET Standard 2.1/.NET Core 2.0
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var (pathFrom, pathTo) in new[] {
      // Windows形式のパス
      (@"E:\", @"E:\dir"),
      (@"E:\", @"E:\dir\"),
      (@"E:\", @"E:\dir\file.txt"),
      (@"E:\dir", @"E:\"),
      (@"E:\dir\", @"E:\"),
      (@"E:\dir\file.txt", @"E:\"),
      (@"E:\dir-from", @"E:\dir-to"),
      (@"E:\dir-from", @"E:\dir-to\file-to.txt"),
      (@"E:\dir-from\file-from.txt", @"E:\dir-to\file-to.txt"),
      (@"E:\", @"C:\"),
      (@"E:\dir-from\file-from.txt", @"C:\dir-to\file-to.txt"),
      // UNIX形式のパス
      ("/", "/dir"),
      ("/", "/dir/"),
      ("/", "/dir/file.txt"),
      ("/dir", "/"),
      ("/dir/", "/"),
      ("/dir/file.txt", "/"),
      ("/dir-from", "/dir-to"),
      ("/dir-from", "/dir-to/file-to.txt"),
      ("/dir-from/file-from.txt", "/dir-to/file-to.txt"),
    }) {
      Console.WriteLine($"path from = {pathFrom}");
      Console.WriteLine($"path to   = {pathTo}");
      Console.WriteLine($"relative  = {Path.GetRelativePath(pathFrom, pathTo)}");
      Console.WriteLine();
    }
  }
}
Path.GetRelativePathメソッドでパス間の相対パスを取得する  .NET Standard 2.1/.NET Core 2.0
Option Infer On

Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each pair In New (String, String)() {
      ("E:\", "E:\dir"), ' Windows形式のパス
      ("E:\", "E:\dir\"),
      ("E:\", "E:\dir\file.txt"),
      ("E:\dir", "E:\"),
      ("E:\dir\", "E:\"),
      ("E:\dir\file.txt", "E:\"),
      ("E:\dir-from", "E:\dir-to"),
      ("E:\dir-from", "E:\dir-to\file-to.txt"),
      ("E:\dir-from\file-from.txt", "E:\dir-to\file-to.txt"),
      ("E:\", "C:\"),
      ("E:\dir-from\file-from.txt", "C:\dir-to\file-to.txt"),
      ("/", "/dir"), ' UNIX形式のパス
      ("/", "/dir/"),
      ("/", "/dir/file.txt"),
      ("/dir", "/"),
      ("/dir/", "/"),
      ("/dir/file.txt", "/"),
      ("/dir-from", "/dir-to"),
      ("/dir-from", "/dir-to/file-to.txt"),
      ("/dir-from/file-from.txt", "/dir-to/file-to.txt")
    }
      Dim pathFrom = pair.Item1
      Dim pathTo = pair.Item2

      Console.WriteLine($"path from = {pathFrom}")
      Console.WriteLine($"path to   = {pathTo}")
      Console.WriteLine($"relative  = {Path.GetRelativePath(pathFrom, pathTo)}")
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path from = E:\
path to   = E:\dir
relative  = dir

path from = E:\
path to   = E:\dir\
relative  = dir\

path from = E:\
path to   = E:\dir\file.txt
relative  = dir\file.txt

path from = E:\dir
path to   = E:\
relative  = ..

path from = E:\dir\
path to   = E:\
relative  = ..

path from = E:\dir\file.txt
path to   = E:\
relative  = ..\..

path from = E:\dir-from
path to   = E:\dir-to
relative  = ..\dir-to

path from = E:\dir-from
path to   = E:\dir-to\file-to.txt
relative  = ..\dir-to\file-to.txt

path from = E:\dir-from\file-from.txt
path to   = E:\dir-to\file-to.txt
relative  = ..\..\dir-to\file-to.txt

path from = E:\
path to   = C:\
relative  = C:\

path from = E:\dir-from\file-from.txt
path to   = C:\dir-to\file-to.txt
relative  = C:\dir-to\file-to.txt

path from = /
path to   = /dir
relative  = dir

path from = /
path to   = /dir/
relative  = dir\

path from = /
path to   = /dir/file.txt
relative  = dir\file.txt

path from = /dir
path to   = /
relative  = ..

path from = /dir/
path to   = /
relative  = ..

path from = /dir/file.txt
path to   = /
relative  = ..\..

path from = /dir-from
path to   = /dir-to
relative  = ..\dir-to

path from = /dir-from
path to   = /dir-to/file-to.txt
relative  = ..\dir-to\file-to.txt

path from = /dir-from/file-from.txt
path to   = /dir-to/file-to.txt
relative  = ..\..\dir-to\file-to.txt

実行結果
path from = E:\
path to   = E:\dir
relative  = ../E:\dir

path from = E:\
path to   = E:\dir\
relative  = ../E:\dir\

path from = E:\
path to   = E:\dir\file.txt
relative  = ../E:\dir\file.txt

path from = E:\dir
path to   = E:\
relative  = ../E:\

path from = E:\dir\
path to   = E:\
relative  = ../E:\

path from = E:\dir\file.txt
path to   = E:\
relative  = ../E:\

path from = E:\dir-from
path to   = E:\dir-to
relative  = ../E:\dir-to

path from = E:\dir-from
path to   = E:\dir-to\file-to.txt
relative  = ../E:\dir-to\file-to.txt

path from = E:\dir-from\file-from.txt
path to   = E:\dir-to\file-to.txt
relative  = ../E:\dir-to\file-to.txt

path from = E:\
path to   = C:\
relative  = ../C:\

path from = E:\dir-from\file-from.txt
path to   = C:\dir-to\file-to.txt
relative  = ../C:\dir-to\file-to.txt

path from = /
path to   = /dir
relative  = dir

path from = /
path to   = /dir/
relative  = dir/

path from = /
path to   = /dir/file.txt
relative  = dir/file.txt

path from = /dir
path to   = /
relative  = ..

path from = /dir/
path to   = /
relative  = ..

path from = /dir/file.txt
path to   = /
relative  = ../..

path from = /dir-from
path to   = /dir-to
relative  = ../dir-to

path from = /dir-from
path to   = /dir-to/file-to.txt
relative  = ../dir-to/file-to.txt

path from = /dir-from/file-from.txt
path to   = /dir-to/file-to.txt
relative  = ../../dir-to/file-to.txt

絶対パスを取得するにはGetFullPathメソッドを使用します。

実行環境によってパスにおける大文字小文字の違いを意識/無視するかは異なり、例えばWindowsではdirDIRは同じパスを表すと判断される一方、それ以外では異なるパスと判断される場合があります。

GetRelativePathメソッドは実行環境での大文字小文字の扱い方に応じて相対パスを作成します。 GetRelativePathメソッドでは大文字小文字の違いを意識/無視するかどうか、あるいはパスの比較に用いるIEqualityComparer<String>等を明示的に指定することはできません。

なお、GetRelativePathメソッドでのパス間の比較は、StringComparison.Ordinal/OrdinalIgnoreCaseでの比較が行われます。

Path.GetRelativePathメソッドでは実行環境に応じてパスの大文字小文字が区別/同視される  .NET Standard 2.1/.NET Core 2.0
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var (pathFrom, pathTo) in new[] {
      // Windows形式のパス
      (@"\dir\from", @"\dir\to"), // 大文字小文字の違いなし
      (@"\dir\from", @"\DIR\to"), // 大文字小文字の違いあり
      // UNIX形式のパス
      ("/dir/from", "/dir/to"), // 大文字小文字の違いなし
      ("/dir/from", "/DIR/to"), // 大文字小文字の違いあり
    }) {
      Console.WriteLine($"path from = {pathFrom}");
      Console.WriteLine($"path to   = {pathTo}");
      Console.WriteLine($"relative  = {Path.GetRelativePath(pathFrom, pathTo)}");
      Console.WriteLine();
    }
  }
}
Path.GetRelativePathメソッドでは実行環境に応じてパスの大文字小文字が区別/同視される  .NET Standard 2.1/.NET Core 2.0
Option Infer On

Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each pair In New (String, String)() {
      ("\dir\from", "\dir\to"), ' Windows形式のパス・大文字小文字の違いなし
      ("\dir\from", "\DIR\to"), ' Windows形式のパス・大文字小文字の違いあり
      ("/dir/from", "/dir/to"), ' UNIX形式のパス・大文字小文字の違いなし
      ("/dir/from", "/DIR/to")  ' UNIX形式のパス・大文字小文字の違いあり
    }
      Dim pathFrom = pair.Item1
      Dim pathTo = pair.Item2

      Console.WriteLine($"path from = {pathFrom}")
      Console.WriteLine($"path to   = {pathTo}")
      Console.WriteLine($"relative  = {Path.GetRelativePath(pathFrom, pathTo)}")
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path from = \dir\from
path to   = \dir\to
relative  = ..\to

path from = \dir\from
path to   = \DIR\to
relative  = ..\to

path from = /dir/from
path to   = /dir/to
relative  = ..\to

path from = /dir/from
path to   = /DIR/to
relative  = ..\to

実行結果
path from = \dir\from
path to   = \dir\to
relative  = ../\dir\to

path from = \dir\from
path to   = \DIR\to
relative  = ../\DIR\to

path from = /dir/from
path to   = /dir/to
relative  = ../to

path from = /dir/from
path to   = /DIR/to
relative  = ../../DIR/to

厳密には、大文字小文字の扱われ方はファイルシステムごとに異なる一方、GetRelativePathメソッドはパスがどのファイルシステム上のエントリを表すものか、またそのファイルシステムでの扱われ方については関知しません。

Uriクラスを使った相対パスの取得

Pathクラスには絶対パスから相対パスを取得するメソッドは用意されていません。 代わりにUriクラスを使って相対パスを作成することができます。 以下に紹介する方法では、ファイルパスをURIとして扱い、Uriクラスのメソッドを使って相対パスに変換しています。

Uriクラスを使ってパス間の相対パスを求める
using System;
using System.IO;

class Sample {
  // basePathを基準としてpathへの相対パスを取得するメソッド
  static string GetRelativePath(string basePath, string path)
  {
    if (basePath == null)
      throw new ArgumentNullException("basePath");
    if (path == null)
      throw new ArgumentNullException("path");

    // 現在Windows上で動作しているかどうか
    var isRunningOnWindows = (int)Environment.OSVersion.Platform < 4;

    if (isRunningOnWindows && !Path.IsPathRooted(basePath))
      throw new ArgumentException("パスは絶対パスである必要があります", "basePath");

    // URIとして処理する前にパス中の%をURLエンコードする
    basePath = basePath.Replace("%", "%25");
    path     = path    .Replace("%", "%25");

    if (!isRunningOnWindows) {
      // 非Windows環境ではパスをURIとして解釈させるためにfile://スキームを前置する
      basePath = Uri.UriSchemeFile + Uri.SchemeDelimiter + basePath.Replace(":", "%3A");
      path     = Uri.UriSchemeFile + Uri.SchemeDelimiter + path    .Replace(":", "%3A");
    }

    // パスをURIに変換
    var uriBase = new Uri(basePath);
    var uriTarget = new Uri(path);

    // MakeRelativeUriメソッドで相対パスを取得する
    // 同時にURIに変換する際にエスケープされた文字列をアンエスケープする
    var relativePath = Uri.UnescapeDataString(uriBase.MakeRelativeUri(uriTarget).ToString());

    // ディレクトリ区切り文字を環境に合わせて置換する
    relativePath = relativePath.Replace('/', Path.DirectorySeparatorChar);

    // URLエンコードした%を元に戻す
    return relativePath.Replace("%25", "%");
  }

  static void Main()
  {
    var paths = (int)Environment.OSVersion.Platform < 4
      ? // Windows形式のパス
        new[] {
          (@"E:\", @"E:\dir"),
          (@"E:\", @"E:\dir\"),
          (@"E:\", @"E:\dir\file.txt"),
          (@"E:\dir", @"E:\"),
          (@"E:\dir\", @"E:\"),
          (@"E:\dir\file.txt", @"E:\"),
          (@"E:\dir-from", @"E:\dir-to"),
          (@"E:\dir-from", @"E:\dir-to\file-to.txt"),
          (@"E:\dir-from\file-from.txt", @"E:\dir-to\file-to.txt"),
          (@"E:\", @"C:\"),
          (@"E:\dir-from\file-from.txt", @"C:\dir-to\file-to.txt"),
        }
      : // UNIX形式のパス
        new[] {
          (@"/", @"/dir"),
          (@"/", @"/dir/"),
          (@"/", @"/dir/file.txt"),
          (@"/dir", @"/"),
          (@"/dir/", @"/"),
          (@"/dir/file.txt", @"/"),
          (@"/dir-from", @"/dir-to"),
          (@"/dir-from", @"/dir-to/file-to.txt"),
          (@"/dir-from/file-from.txt", @"/dir-to/file-to.txt"),
         };

    foreach (var (pathFrom, pathTo) in paths) {
      Console.WriteLine($"path from = {pathFrom}");
      Console.WriteLine($"path to   = {pathTo}");
      Console.WriteLine($"relative  = {GetRelativePath(pathFrom, pathTo)}");
      Console.WriteLine();
    }
  }
}
実行結果
path from = E:\
path to   = E:\dir
relative  = dir

path from = E:\
path to   = E:\dir\
relative  = dir\

path from = E:\
path to   = E:\dir\file.txt
relative  = dir\file.txt

path from = E:\dir
path to   = E:\
relative  = .\

path from = E:\dir\
path to   = E:\
relative  = ..\

path from = E:\dir\file.txt
path to   = E:\
relative  = ..\

path from = E:\dir-from
path to   = E:\dir-to
relative  = dir-to

path from = E:\dir-from
path to   = E:\dir-to\file-to.txt
relative  = dir-to\file-to.txt

path from = E:\dir-from\file-from.txt
path to   = E:\dir-to\file-to.txt
relative  = ..\dir-to\file-to.txt

path from = E:\
path to   = C:\
relative  = C:\

path from = E:\dir-from\file-from.txt
path to   = C:\dir-to\file-to.txt
relative  = C:\dir-to\file-to.txt

実行結果
path from = /
path to   = /dir
relative  = dir

path from = /
path to   = /dir/
relative  = dir/

path from = /
path to   = /dir/file.txt
relative  = dir/file.txt

path from = /dir
path to   = /
relative  = ./

path from = /dir/
path to   = /
relative  = ../

path from = /dir/file.txt
path to   = /
relative  = ../

path from = /dir-from
path to   = /dir-to
relative  = dir-to

path from = /dir-from
path to   = /dir-to/file-to.txt
relative  = dir-to/file-to.txt

path from = /dir-from/file-from.txt
path to   = /dir-to/file-to.txt
relative  = ../dir-to/file-to.txt

ルートディレクトリの取得・ルートディレクトリからのパスかどうか (GetPathRoot/IsPathRooted)

GetPathRootメソッドを使うことでパスからルートディレクトリを取得することができます。 ドライブ名を含むパスの場合は、ドライブ名も含めたルートディレクトリが返されます。 例えば、パスE:\dirの場合はEドライブのルートディレクトリE:\が返されます。 カレントドライブからの相対パス\dirでは、ドライブ名の指定がないルートディレクトリ\のみが返されます。 (カレントディレクトリからの)相対パスなど、ルートディレクトリからのパスでない場合は、空の文字列が返されます。

また、パスがルートディレクトリから始まっているかどうか(ルートディレクトリからのパスであるか)を調べるにはIsPathRootedメソッドを使うことができます。 ルートディレクトリとして判断される基準はGetPathRootメソッドと同様です。

GetPathRoot・IsPathRootedメソッドではあくまでパス形式としての検証のみが行われるため、実在しないルートディレクトリの場合でも例外は発生しません。

Path.GetPathRoot/IsPathRootedメソッドでルートディレクトリを抽出する/ルートディレクトリからのパスか調べる
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",      // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",        // 絶対パス (UNIX形式)
      @"\\127.0.0.1\e$\dir\file.txt", // UNCパス
      @"\dir\subdir\file.txt",        // 相対パス (Windows形式・カレントドライブからの相対パス)
      @".\dir\subdir\file.txt",       // 相対パス (Windows形式・カレントディレクトリからの相対パス)
      @"./dir/subdir/file.txt",       // 相対パス (UNIX形式)
      @"file.txt",                    // ファイル名のみのパス
    }) {
      Console.WriteLine("path  = {0}", path);
      Console.WriteLine("root  = {0}", Path.GetPathRoot(path)); // パスからルートディレクトリを抽出する
      Console.WriteLine("rooted? {0}", Path.IsPathRooted(path)); // パスがルートディレクトリから始まっているかどうか調べる
      Console.WriteLine();
    }
  }
}
Path.GetPathRoot/IsPathRootedメソッドでルートディレクトリを抽出する/ルートディレクトリからのパスか調べる
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt",       ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",         ' 絶対パス (UNIX形式)
      "\\127.0.0.1\e$\dir\file.txt",  ' UNCパス
      "\dir\subdir\file.txt",         ' 相対パス (Windows形式・カレントドライブからの相対パス)
      ".\dir\subdir\file.txt",        ' 相対パス (Windows形式・カレントディレクトリからの相対パス)
      "./dir/subdir/file.txt",        ' 相対パス (UNIX形式)
      "file.txt"                      ' ファイル名のみのパス
    }
      Console.WriteLine("path  = {0}", p)
      Console.WriteLine("root  = {0}", Path.GetPathRoot(p)) ' パスからルートディレクトリを抽出する
      Console.WriteLine("rooted? {0}", Path.IsPathRooted(p)) ' パスがルートディレクトリから始まっているかどうか調べる
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path  = E:\dir\subdir\file.txt
root  = E:\
rooted? True

path  = /dir/subdir/file.txt
root  = \
rooted? True

path  = \\127.0.0.1\e$\dir\file.txt
root  = \\127.0.0.1\e$
rooted? True

path  = \dir\subdir\file.txt
root  = \
rooted? True

path  = .\dir\subdir\file.txt
root  =
rooted? False

path  = ./dir/subdir/file.txt
root  =
rooted? False

path  = file.txt
root  =
rooted? False

実行結果
path  = E:\dir\subdir\file.txt
root  = 
rooted? False

path  = /dir/subdir/file.txt
root  = /
rooted? True

path  = \\127.0.0.1\e$\dir\file.txt
root  = 
rooted? False

path  = \dir\subdir\file.txt
root  = 
rooted? False

path  = .\dir\subdir\file.txt
root  = 
rooted? False

path  = ./dir/subdir/file.txt
root  = 
rooted? False

path  = file.txt
root  = 
rooted? False

パスがUNCパスなどの完全修飾になっているかどうかを調べるにはPath.IsPathFullyQualifiedメソッドを使うことができます。

完全修飾のパスかどうか (IsPathFullyQualified)

パスが完全修飾されている(fully-qualified)かどうかを調べるにはIsPathFullyQualifiedメソッドを使うことができます。 このメソッドでは、パスが特定のドライブ/デバイス/ボリューム内のディレクトリ/ファイルを表すものかどうかを判定します。

このため、UNCパスやDOSデバイスパスなど常に完全修飾となる形式のパスや、ドライブ名から始まるパスE:\dirなどの場合にtrue(完全修飾されている)を返します。 ルートディレクトリから始まるパス\dir, /dirなどは、Windows環境ではfalse(完全修飾されていない)が返されます。 非Windows環境では、ルートディレクトリ/から始まるパスに対してはtrue(完全修飾されている)を返します。

このメソッドではあくまでパス形式としての検証のみが行われるため、実在しないドライブ/デバイス/ボリュームの場合でも例外は発生しません。 また非Windows環境では、ルートディレクトリ/から始まるパスのみが完全修飾されているものとして判断され、UNCパスやDOSデバイスパスは解釈されず、完全修飾されていないものと判断されます。

なお、IsPathFullyQualifiedメソッドは.NET Standard 2.1/.NET Core 2.1以降で使用できます。

Path.IsPathFullyQualifiedメソッドで完全修飾されたパスかどうか調べる .NET Standard 2.1/.NET Core 2.1
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      @"E:\dir\subdir\file.txt",      // 絶対パス (Windows形式)
      @"/dir/subdir/file.txt",        // 絶対パス (UNIX形式)
      @"\\127.0.0.1\e$\dir\file.txt", // UNCパス
      @"\\.\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\dir\file.txt", // DOSデバイスパス
      @"\dir\subdir\file.txt",        // 相対パス (Windows形式・カレントドライブからの相対パス)
      @".\dir\subdir\file.txt",       // 相対パス (Windows形式・カレントディレクトリからの相対パス)
      @"./dir/subdir/file.txt",       // 相対パス (UNIX形式)
      @"file.txt",                    // ファイル名のみのパス
    }) {
      Console.WriteLine("path = {0}", path);
      Console.WriteLine("fully qualified? {0}", Path.IsPathFullyQualified(path)); // パスが完全修飾となっているか調べる
      Console.WriteLine();
    }
  }
}
Path.IsPathFullyQualifiedメソッドで完全修飾されたパスかどうか調べる .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\file.txt",       ' 絶対パス (Windows形式)
      "/dir/subdir/file.txt",         ' 絶対パス (UNIX形式)
      "\\127.0.0.1\e$\dir\file.txt",  ' UNCパス
      "\dir\subdir\file.txt",         ' 相対パス (Windows形式・カレントドライブからの相対パス)
      ".\dir\subdir\file.txt",        ' 相対パス (Windows形式・カレントディレクトリからの相対パス)
      "./dir/subdir/file.txt",        ' 相対パス (UNIX形式)
      "file.txt"                      ' ファイル名のみのパス
    }
      Console.WriteLine("path = {0}", p);
      Console.WriteLine("fully qualified? {0}", Path.IsPathFullyQualified(p)) ' パスが完全修飾となっているか調べる
      Console.WriteLine();
    Next
  End Sub
End Class
実行結果
path = E:\dir\subdir\file.txt
fully qualified? True

path = /dir/subdir/file.txt
fully qualified? False

path = \\127.0.0.1\e$\dir\file.txt
fully qualified? True

path = \\.\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\dir\file.txt
fully qualified? True

path = \dir\subdir\file.txt
fully qualified? False

path = .\dir\subdir\file.txt
fully qualified? False

path = ./dir/subdir/file.txt
fully qualified? False

path = file.txt
fully qualified? False

実行結果
path = E:\dir\subdir\file.txt
fully qualified? False

path = /dir/subdir/file.txt
fully qualified? True

path = \\127.0.0.1\e$\dir\file.txt
fully qualified? False

path = \\.\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\dir\file.txt
fully qualified? False

path = \dir\subdir\file.txt
fully qualified? False

path = .\dir\subdir\file.txt
fully qualified? False

path = ./dir/subdir/file.txt
fully qualified? False

path = file.txt
fully qualified? False

ディレクトリ区切り文字で終端されているか・終端をトリミングする (EndsInDirectorySeparator/TrimEndingDirectorySeparator)

パスの末尾がディレクトリ区切り文字となっているかどうかを調べるにはEndsInDirectorySeparatorメソッドを使うことができます。 また、ディレクトリ区切り文字で終端されているパスから終端のディレクトリ区切り文字をトリミングするにはTrimEndingDirectorySeparatorメソッドを使うことができます。

EndsInDirectorySeparator・TrimEndingDirectorySeparatorメソッドは、ディレクトリを表すパスの場合は末尾が\あるいは/となっていることを要求したい場合、逆にパスがファイルでもディレクトリでも同一の表記で扱いたい場合などに用いることができます。

TrimEndingDirectorySeparatorメソッドは、E:\,\,/などルートディレクトリを表すパスの場合は終端のトリミングは行いません。 そのため、このメソッドを使ってドライブ名(E:)のみを取得することはできません。

なお、EndsInDirectorySeparator・TrimEndingDirectorySeparatorメソッドは.NET Standard 2.1/.NET Core 2.1以降で使用できます。

Path.TrimEndingDirectorySeparator/EndsInDirectorySeparatorメソッドでパス末尾のディレクトリ区切り文字を除去する/パス末尾がディレクトリ区切り文字かどうか調べる .NET Standard 2.1/.NET Core 2.1
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      // ディレクトリパス (Windows形式)
      @"E:\dir\subdir\",
      @"E:\dir\subdir",
      @"E:\",
      @"\",
      @"dir\subdir\",
      @"dir\subdir",
      // ディレクトリパス (Unix形式)
      @"/",
      @"/dir/subdir/",
      @"/dir/subdir",
      @"dir/subdir/",
      @"dir/subdir",
    }) {
      Console.WriteLine("path         = {0}", path);
      Console.WriteLine("dir separator? {0}", Path.EndsInDirectorySeparator(path)); // パスがディレクトリ区切り文字で終わるかどうか調べる
      Console.WriteLine("trimmed path = {0}", Path.TrimEndingDirectorySeparator(path)); // パス末尾のディレクトリ区切り文字を除去する
      Console.WriteLine();
    }
  }
}
Path.TrimEndingDirectorySeparator/EndsInDirectorySeparatorメソッドでパス末尾のディレクトリ区切り文字を除去する/パス末尾がディレクトリ区切り文字かどうか調べる .NET Standard 2.1/.NET Core 2.1
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\", ' ディレクトリパス (Windows形式)
      "E:\dir\subdir",
      "E:\",
      "\",
      "dir\subdir\",
      "dir\subdir",
      "/",  ' ディレクトリパス (Unix形式)
      "/dir/subdir/",
      "/dir/subdir",
      "dir/subdir/",
      "dir/subdir"
    }
      Console.WriteLine("path         = {0}", p)
      Console.WriteLine("dir separator? {0}", Path.EndsInDirectorySeparator(p)) ' パスがディレクトリ区切り文字で終わるかどうか調べる
      Console.WriteLine("trimmed path = {0}", Path.TrimEndingDirectorySeparator(p)) ' パス末尾のディレクトリ区切り文字を除去する
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path         = E:\dir\subdir\
dir separator? True
trimmed path = E:\dir\subdir

path         = E:\dir\subdir
dir separator? False
trimmed path = E:\dir\subdir

path         = E:\
dir separator? True
trimmed path = E:\

path         = \
dir separator? True
trimmed path = \

path         = dir\subdir\
dir separator? True
trimmed path = dir\subdir

path         = dir\subdir
dir separator? False
trimmed path = dir\subdir

path         = /
dir separator? True
trimmed path = /

path         = /dir/subdir/
dir separator? True
trimmed path = /dir/subdir

path         = /dir/subdir
dir separator? False
trimmed path = /dir/subdir

path         = dir/subdir/
dir separator? True
trimmed path = dir/subdir

path         = dir/subdir
dir separator? False
trimmed path = dir/subdir

実行結果
path         = E:\dir\subdir\
dir separator? False
trimmed path = E:\dir\subdir\

path         = E:\dir\subdir
dir separator? False
trimmed path = E:\dir\subdir

path         = E:\
dir separator? False
trimmed path = E:\

path         = \
dir separator? False
trimmed path = \

path         = dir\subdir\
dir separator? False
trimmed path = dir\subdir\

path         = dir\subdir
dir separator? False
trimmed path = dir\subdir

path         = /
dir separator? True
trimmed path = /

path         = /dir/subdir/
dir separator? True
trimmed path = /dir/subdir

path         = /dir/subdir
dir separator? False
trimmed path = /dir/subdir

path         = dir/subdir/
dir separator? True
trimmed path = dir/subdir

path         = dir/subdir
dir separator? False
trimmed path = dir/subdir

EndsInDirectorySeparator・TrimEndingDirectorySeparatorメソッドが使用できない場合は、DirectorySeparatorChar・AltDirectorySeparatorCharフィールドString.EndsWithメソッドString.TrimEndメソッドを組み合わせることで同様の処理を行うことができます。

String.TrimEnd/EndsWithメソッドを使ってパス末尾のディレクトリ区切り文字を除去する/パス末尾がディレクトリ区切り文字かどうか調べる
using System;
using System.IO;

class Sample {
  static void Main()
  {
    foreach (var path in new[] {
      // ディレクトリパス (Windows形式)
      @"E:\dir\subdir\",
      @"E:\dir\subdir",
      @"E:\",
      @"\",
      @"dir\subdir\",
      @"dir\subdir",
      // ディレクトリパス (Unix形式)
      @"/",
      @"/dir/subdir/",
      @"/dir/subdir",
      @"dir/subdir/",
      @"dir/subdir",
    }) {
      Console.WriteLine("path         = {0}", path);
      // パスがディレクトリ区切り文字で終わるかどうか調べる
      // (Path.EndsInDirectorySeparatorメソッド相当の処理)
      Console.WriteLine("dir separator? {0}", path.EndsWith(Path.DirectorySeparatorChar) || path.EndsWith(Path.AltDirectorySeparatorChar));
      // パス末尾のディレクトリ区切り文字を除去する
      // (Path.TrimEndingDirectorySeparatorメソッド相当の処理、ただし
      // この実装ではルートディレクトリを表すパスの場合も末尾の区切り文字が
      // 削除されてしまうため、別途対処が必要となる)
      Console.WriteLine("trimmed path = {0}", path.TrimEnd(new[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}));
      Console.WriteLine();
    }
  }
}
String.TrimEnd/EndsWithメソッドを使ってパス末尾のディレクトリ区切り文字を除去する/パス末尾がディレクトリ区切り文字かどうか調べる
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    For Each p As String In New String() {
      "E:\dir\subdir\", ' ディレクトリパス (Windows形式)
      "E:\dir\subdir",
      "E:\",
      "\",
      "dir\subdir\",
      "dir\subdir",
      "/",  ' ディレクトリパス (Unix形式)
      "/dir/subdir/",
      "/dir/subdir",
      "dir/subdir/",
      "dir/subdir"
    }
      Console.WriteLine("path         = {0}", p)
      ' パスがディレクトリ区切り文字で終わるかどうか調べる
      ' (Path.EndsInDirectorySeparatorメソッド相当の処理)
      Console.WriteLine("dir separator? {0}", p.EndsWith(Path.DirectorySeparatorChar) OrElse p.EndsWith(Path.AltDirectorySeparatorChar))
      ' パス末尾のディレクトリ区切り文字を除去する
      ' (Path.TrimEndingDirectorySeparatorメソッド相当の処理、ただし
      ' この実装ではルートディレクトリを表すパスの場合も末尾の区切り文字が
      ' 削除されてしまうため、別途対処が必要となる)
      Console.WriteLine("trimmed path = {0}", p.TrimEnd(New Char() {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}))
      Console.WriteLine()
    Next
  End Sub
End Class
実行結果
path         = E:\dir\subdir\
dir separator? True
trimmed path = E:\dir\subdir

path         = E:\dir\subdir
dir separator? False
trimmed path = E:\dir\subdir

path         = E:\
dir separator? True
trimmed path = E:

path         = \
dir separator? True
trimmed path =

path         = dir\subdir\
dir separator? True
trimmed path = dir\subdir

path         = dir\subdir
dir separator? False
trimmed path = dir\subdir

path         = /
dir separator? True
trimmed path =

path         = /dir/subdir/
dir separator? True
trimmed path = /dir/subdir

path         = /dir/subdir
dir separator? False
trimmed path = /dir/subdir

path         = dir/subdir/
dir separator? True
trimmed path = dir/subdir

path         = dir/subdir
dir separator? False
trimmed path = dir/subdir

実行結果
path         = E:\dir\subdir\
dir separator? False
trimmed path = E:\dir\subdir\

path         = E:\dir\subdir
dir separator? False
trimmed path = E:\dir\subdir

path         = E:\
dir separator? False
trimmed path = E:\

path         = \
dir separator? False
trimmed path = \

path         = dir\subdir\
dir separator? False
trimmed path = dir\subdir\

path         = dir\subdir
dir separator? False
trimmed path = dir\subdir

path         = /
dir separator? True
trimmed path = 

path         = /dir/subdir/
dir separator? True
trimmed path = /dir/subdir

path         = /dir/subdir
dir separator? False
trimmed path = /dir/subdir

path         = dir/subdir/
dir separator? True
trimmed path = dir/subdir

path         = dir/subdir
dir separator? False
trimmed path = dir/subdir

パスやファイル名に使用できない文字の取得 (GetInvalidPathChars/GetInvalidFileNameChars)

パスやファイル名では、一部の制御文字・記号を使うことができません。 パスとして使用できない文字はGetInvalidPathCharsメソッド、ファイル名として使用できない文字はGetInvalidFileNameCharsメソッドによって、char/Charの配列として取得できます。 このメソッドでは、現在の実行環境において使用できない文字を返します。 GetInvalidFileNameCharsメソッドの返す結果には、ディレクトリ区切り文字は含まれません。

Path.GetInvalidPathChars/GetInvalidFileNameCharsメソッドでパス/ファイル名に使えない文字の一覧を取得する
using System;
using System.IO;
using System.Linq;
using System.Text.Unicode;

class Sample {
  static void Main()
  {
    // ファイル名に使えない文字を取得・表示する
    Console.WriteLine(
      "InvalidPathChars: {0}",
      string.Join(" ", Path.GetInvalidFileNameChars().Select(ToUnicodeControlPictures))
    );

    // パスに使えない文字を取得・表示する
    Console.WriteLine(
      "InvalidFileNameChars: {0}",
      string.Join(" ", Path.GetInvalidPathChars().Select(ToUnicodeControlPictures))
    );

    static char ToUnicodeControlPictures(char c)
      => char.IsControl(c) ? (char)(UnicodeRanges.ControlPictures.FirstCodePoint + (int)c) : c; // Control CodesをControl Picturesに変換する
  }
}
Path.GetInvalidPathChars/GetInvalidFileNameCharsメソッドでパス/ファイル名に使えない文字の一覧を取得する
Imports System
Imports System.IO
Imports System.Linq
Imports System.Text.Unicode

Class Sample
  Shared Sub Main()
    ' ファイル名に使えない文字を取得・表示する
    Console.WriteLine(
      "InvalidPathChars: {0}",
      String.Join(" ", Path.GetInvalidFileNameChars().Select(AddressOf ToUnicodeControlPictures))
    )

    ' パスに使えない文字を取得・表示する
    Console.WriteLine(
      "InvalidFileNameChars: {0}",
      String.Join(" ", Path.GetInvalidPathChars().Select(AddressOf ToUnicodeControlPictures))
    )
  End Sub

  Shared Function ToUnicodeControlPictures(ByVal c As Char) As Char
    Return If(Char.IsControl(c), ChrW(UnicodeRanges.ControlPictures.FirstCodePoint + AscW(c)), c) ' Control CodesをControl Picturesに変換する
  End Function
End Class
実行結果
InvalidPathChars: " < > | ␀ ␁ ␂ ␃ ␄ ␅ ␆ ␇ ␈ ␉ ␊ ␋ ␌ ␍ ␎ ␏ ␐ ␑ ␒ ␓ ␔ ␕ ␖ ␗ ␘ ␙ ␚ ␛ ␜ ␝ ␞ ␟ : * ? \ /
InvalidFileNameChars: | ␀ ␁ ␂ ␃ ␄ ␅ ␆ ␇ ␈ ␉ ␊ ␋ ␌ ␍ ␎ ␏ ␐ ␑ ␒ ␓ ␔ ␕ ␖ ␗ ␘ ␙ ␚ ␛ ␜ ␝ ␞ ␟
上記の結果を16進表記にしたもの
InvalidPathChars: 22 3C 3E 7C 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 3A 2A 3F 5C 2F
InvalidFileNameChars: 7C 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
実行結果
InvalidPathChars: ␀ /
InvalidFileNameChars: ␀
上記の結果を16進表記にしたもの
InvalidPathChars: 00 2F
InvalidFileNameChars: 00

このメソッドが返す配列を用いることにより、例えばユーザーによって入力されたパスやファイル名などのバリデーション(妥当性の検証)を行うことができます。 なお、Pathクラスのメソッドは、パスに使用できない文字が含まれる場合に例外ArgumentExceptionをスローするものがあります。

一方で、Pathクラスにはパス・ファイル名に使用できない文字を別の文字に置き換えるようなメソッドは存在しません。 そのため、パス・ファイル名がユーザー入力などによって与えられるケースでは、何らかのルールを定義して妥当でない文字を別の文字に置換するなどしてからファイル操作を行う必要があります。

シェルや文字コードの種類・組み合わせによってはこれ以外の文字が特殊記号と誤認され、パス・ファイル名を原因とする意図しない動作となる場合があります。 この点についてはダメ文字を含むかどうかチェックするを参照してください。

GetRandomFileNameメソッドGetTempFileNameメソッドを用いることで、妥当かつランダムなファイル名を生成することができます。 使用不可能な文字を含まない適当なファイル名が必要な場合には、これらのメソッドを用いることもできます。

ファイル名の生成

ランダムなファイル名の生成 (GetRandomFileName)

GetRandomFileNameメソッドを使うことにより、ランダムなファイル名が生成できます。 このメソッドは、呼び出しごとにランダムなファイル名を生成します。 ただし、ファイル名としての文字列が生成されるだけで、ファイルそのものは作成されません

このメソッドでは、常にファイル名8文字.拡張子3文字の形式のファイル名が生成されます。 生成されるファイル名はランダムではあるものの、衝突する可能性はあります。 そのため、このファイル名を使って書き込みを行うような場合は、事前にファイルが実在しないか調べ、存在する場合は再生成するなどの対処が必要になります。

また、このメソッドでは拡張子も含めてランダムなファイル名となるため、特定の拡張子でランダムなファイル名を生成したい場合はChangeExtensionメソッドと組み合わせて使用します。

Path.GetRandomFileNameメソッドでランダムなファイル名を生成する
using System;
using System.IO;

class Sample {
  static void Main()
  {
    // ランダムなファイル名を5個生成する
    for (var i = 0; i < 5; i++) {
      Console.WriteLine(Path.GetRandomFileName());
    }
    Console.WriteLine();

    // ランダムなファイル名を、拡張子.txtで5個作成する
    for (var i = 0; i < 5; i++) {
      Console.WriteLine(Path.ChangeExtension(Path.GetRandomFileName(), ".txt"));
    }
  }
}
Path.GetRandomFileNameメソッドでランダムなファイル名を生成する
Imports System
Imports System.IO

Class Sample
  Shared Sub Main()
    ' ランダムなファイル名を5個生成する
    For i As Integer = 1 To 5
      Console.WriteLine(Path.GetRandomFileName())
    Next
    Console.WriteLine()

    ' ランダムなファイル名を、拡張子.txtで5個作成する
    For i As Integer = 1 To 5
      Console.WriteLine(Path.ChangeExtension(Path.GetRandomFileName(), ".txt"))
    Next
  End Sub
End Class
実行結果例
b0zi1hbt.ptt
p2hbs2gr.xgs
p31mnlvk.h32
wpfbmt3u.ocp
ykxxttdp.umw

4fo2wcoc.txt
lsm3pe0k.txt
kczgd02w.txt
ysdpomh3.txt
lw33m1rh.txt

一時ファイルの作成 (GetTempFileName)

GetTempFileNameメソッドを使うことにより、ランダムなファイル名でサイズが0の一時ファイル(temporary file)を生成し、そのパスを取得することができます。

メソッド名はGetTempFileNameとなっているものの、このメソッドは呼び出しごとにファイルを生成し、そのパスを返す動作となっている点に注意してください。 生成されたファイルは一時ディレクトリ(temporary directory)配下に作成されるため、いずれかのタイミングで自動的に削除されることが期待できますが、必要なくなった時点でFile.Deleteメソッドを用いて速やかに削除することもできます。

Path.GetTempFileNameメソッドの動作、使用例については一時ディレクトリを取得する・一時ファイルを作成する §.一時ファイルの作成 (Path.GetTempFileName)を参照してください。