パスからファイル名・ディレクトリ名を抽出したり、パスを結合したりするにはPathクラスを使います。 .NETではパスは文字列として扱うため、パス関連の文字列処理を行う専用クラスとしてPathクラスが用意されています。 Pathクラスでは、ファイル名・ディレクトリ名・拡張子といったパスからの要素の抽出、絶対パス・相対パスなどのパス同士の結合、絶対パス・相対パス間の変換やパス形式の検証などの操作を行うことができます。
URL(URI)を解析済みの状態で保持するUriクラスなどとは異なり、Pathクラスはパス自体は保持せず、パスに関する文字列操作のみを提供するクラスとなっています。 そのため、Pathクラスではインスタンスを作成せず、常にクラスメソッドを呼び出すことによりパス文字列に対する処理を行います。
     Stringクラスのメソッドによる文字列操作でもパスからファイル名・ディレクトリ名の抽出、パス同士の結合といった操作を行うことはできますが、Pathクラスでは.や..などを含む相対パス、プラットフォームごとに異なるディレクトリ区切り文字(\と/)の扱い、大文字小文字の違い(file.txtとFILE.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デバイスパスでは相対パスを扱うことはできません。
| パス形式 | パスの例 | 備考 | |
|---|---|---|---|
| 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:形式のパスを扱うことができます。
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
      );
    }
  }
}
           
          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メソッドでは、パスに含まれる一番最後のディレクトリ区切り文字の位置を区切りとして、ファイル名・ディレクトリ名を抽出します。 パスに該当する部分がないと判断される場合は、空の文字列が返されます。
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();
    }
  }
}
          
         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メソッドは、ファイル名・ディレクトリ名の抽出に際して、それが実際にファイルであるか、ディレクトリであるかを検証しません。 そのため、次の例のようにディレクトリ区切り文字で終わっていないパスは、仮にそれがディレクトリを表すものであってもファイル名として扱われる点に注意が必要です。 逆に言えば、ディレクトリ区切り文字で終わっていない限り、パスの最後の部分は必ずファイル名として扱われます。
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();
    }
  }
}
          
         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メソッドは空の文字列を返します。
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();
    }
  }
}
          
         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/では、ディレクトリを表すと判断されるため、これに従い拡張子はないものとして扱われます。
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();
    }
  }
}
          
         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)は、常に拡張子を持つものとして判断されます。
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();
    }
  }
}
           
          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メソッドを参照してください。
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"));
  }
}
          
         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のファイル名に変換されることになるため、注意が必要です。
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();
    }
  }
}
          
         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メソッドなどを組み合わせて独自に実装する必要があります。
以下はパスのうち、拡張子を除くファイル名部分を置き換えるメソッドを実装した例です。
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();
    }
  }
}
          
         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メソッドでパスからルートディレクトリを取得できるので、これを使うことでドライブ名を取得することができます。
ただし、完全修飾されているパスの場合はホスト名を含めたドライブ名までの部分が返されるため、単にドライブ名だけを取得することはできません。 また、カレントドライブからの相対パスの場合も、ドライブのルートディレクトリ\が返されるため、ドライブ名は取得できません。 ルートディレクトリを含まない相対パスの場合は、空の文字列が返されます。
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();
    }
  }
}
          
         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メソッドで配列に変換する必要があります。
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()));
  }
}
          
         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.IsPathRootedがtrueとなるパス)が指定されている場合、そのうちの一番最後をルートディレクトリとして選択した上でパスの結合を行います。 それより前のパスは結合に際して用いられず、無視されます。
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の場合)同上、最初の引数は無視される
  }
}
          
         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メソッドを使用します。
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();
  }
}
          
         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クラスで処理した場合、ディレクトリ区切り文字が\に置き換えられる場合があります。
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));
  }
}
          
         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クラスでは複数パスを連結・分割するようなメソッドは直接提供されませんが、環境変数を扱う場合などにはこのフィールドから適切な区切り文字を取得することができます。
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" }));
  }
}
          
         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
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);
    }
  }
}
          
         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に相対パスを指定することはできません
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();
    }
  }
}
          
         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メソッドを呼び出した場合、相対パスは常にカレントディレクトリを基準として解決されます。
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();
    }
  }
}
          
         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:\from→T:\toの場合はT:\to) 同一ドライブ内のパス同士の場合でも、カレントドライブからの相対パス形式とはならず、ディレクトリ間の相対パスとして結果が返されます。 (E:\from→E:\toの場合は\toではなく..\to) 
なお、GetRelativePathメソッドは.NET Standard 2.1/.NET Core 2.0以降で使用できます。 GetRelativePathメソッドが使用できない場合は、Uriクラスを使ってURIとして処理することにより相対パスを得られます。
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();
    }
  }
}
          
         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ではdirとDIRは同じパスを表すと判断される一方、それ以外では異なるパスと判断される場合があります。
GetRelativePathメソッドは実行環境での大文字小文字の扱い方に応じて相対パスを作成します。 GetRelativePathメソッドでは大文字小文字の違いを意識/無視するかどうか、あるいはパスの比較に用いるIEqualityComparer<String>等を明示的に指定することはできません。
なお、GetRelativePathメソッドでのパス間の比較は、StringComparison.Ordinal/OrdinalIgnoreCaseでの比較が行われます。
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();
    }
  }
}
          
         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クラスのメソッドを使って相対パスに変換しています。
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メソッドではあくまでパス形式としての検証のみが行われるため、実在しないルートディレクトリの場合でも例外は発生しません。
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();
    }
  }
}
          
         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以降で使用できます。
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();
    }
  }
}
          
         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以降で使用できます。
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();
    }
  }
}
          
         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メソッドを組み合わせることで同様の処理を行うことができます。
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();
    }
  }
}
          
         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メソッドの返す結果には、ディレクトリ区切り文字は含まれません。
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に変換する
  }
}
          
         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: | ␀ ␁ ␂ ␃ ␄ ␅ ␆ ␇ ␈ ␉ ␊ ␋ ␌ ␍ ␎ ␏ ␐ ␑ ␒ ␓ ␔ ␕ ␖ ␗ ␘ ␙ ␚ ␛ ␜ ␝ ␞ ␟
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: ␀
InvalidPathChars: 00 2F InvalidFileNameChars: 00
このメソッドが返す配列を用いることにより、例えばユーザーによって入力されたパスやファイル名などのバリデーション(妥当性の検証)を行うことができます。 なお、Pathクラスのメソッドは、パスに使用できない文字が含まれる場合に例外ArgumentExceptionをスローするものがあります。
一方で、Pathクラスにはパス・ファイル名に使用できない文字を別の文字に置き換えるようなメソッドは存在しません。 そのため、パス・ファイル名がユーザー入力などによって与えられるケースでは、何らかのルールを定義して妥当でない文字を別の文字に置換するなどしてからファイル操作を行う必要があります。
シェルや文字コードの種類・組み合わせによってはこれ以外の文字が特殊記号と誤認され、パス・ファイル名を原因とする意図しない動作となる場合があります。 この点についてはダメ文字を含むかどうかチェックするを参照してください。
GetRandomFileNameメソッドやGetTempFileNameメソッドを用いることで、妥当かつランダムなファイル名を生成することができます。 使用不可能な文字を含まない適当なファイル名が必要な場合には、これらのメソッドを用いることもできます。
ファイル名の生成
ランダムなファイル名の生成 (GetRandomFileName)
GetRandomFileNameメソッドを使うことにより、ランダムなファイル名が生成できます。 このメソッドは、呼び出しごとにランダムなファイル名を生成します。 ただし、ファイル名としての文字列が生成されるだけで、ファイルそのものは作成されません。
このメソッドでは、常にファイル名8文字.拡張子3文字の形式のファイル名が生成されます。 生成されるファイル名はランダムではあるものの、衝突する可能性はあります。 そのため、このファイル名を使って書き込みを行うような場合は、事前にファイルが実在しないか調べ、存在する場合は再生成するなどの対処が必要になります。
また、このメソッドでは拡張子も含めてランダムなファイル名となるため、特定の拡張子でランダムなファイル名を生成したい場合はChangeExtensionメソッドと組み合わせて使用します。
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"));
    }
  }
}
          
         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)を参照してください。