Windows システムのファイル パス形式File path formats on Windows systems

System.IO 名前空間の型の多くのメンバーには path パラメーターが含まれています。このパラメーターによって、ファイル システム リソースの絶対パスまたは相対パスを指定できます。Members of many of the types in the System.IO namespace include a path parameter that lets you specify an absolute or relative path to a file system resource. このパスはその後、Windows ファイル システム API に渡されます。This path is then passed to Windows file system APIs. このトピックでは、Windows システムで利用できるファイル パスの形式について説明します。This topic discusses the formats for file paths that you can use on Windows systems.

従来の DOS パスTraditional DOS paths

標準 DOS パスは 3 つの要素で構成できます。A standard DOS path can consist of three components:

  • ボリュームまたはドライブ文字とそれに続くボリューム区切り記号 (:)。A volume or drive letter followed by the volume separator (:).
  • ディレクトリ名。A directory name. ディレクトリ区切り文字によって、入れ子になっているディレクトリ階層内でサブディレクトリが分割されます。The directory separator character separates subdirectories within the nested directory hierarchy.
  • 任意のファイル名。An optional filename. ディレクトリ区切り文字によって、ファイル パスとファイル名が分割されます。The directory separator character separates the file path and the filename.

3 つのコンポーネントがすべて存在する場合、パスは絶対パスになります。If all three components are present, the path is absolute. ボリュームまたはドライブ文字が指定されておらず、ディレクトリ名がディレクトリ区切り文字で始まる場合、パスは現在のドライブのルートからの相対パスとなります。If no volume or drive letter is specified and the directory name begins with the directory separator character, the path is relative from the root of the current drive. それ以外の場合、パスは現在のディレクトリに相対となります。Otherwise, the path is relative to the current directory. 次の表では、想定されるディレクトリとファイル パスをいくつかまとめています。The following table shows some possible directory and file paths.

パスPath 説明Description
C:\Documents\Newsletters\Summer2018.pdf ドライブ C: のルートからの絶対ファイル パス。An absolute file path from the root of drive C:
\Program Files\Custom Utilities\StringFinder.exe 現在のドライブのルートからの絶対パス。An absolute path from the root of the current drive.
2018\January.xlsx 現在のディレクトリのサブディレクトリにあるファイルへの相対パス。A relative path to a file in a subdirectory of the current directory.
..\Publications\TravelBrochure.pdf 現在のディレクトリと同等になるディレクトリにあるファイルへの相対パス。A relative path to file in a directory that is a peer of the current directory.
C:\Projects\apilibrary\apilibrary.sln ドライブ C: のルートからのファイルへの絶対パス。An absolute path to a file from the root of drive C:
C:Projects\apilibrary\apilibrary.sln C: ドライブの現在のディレクトリからの相対パス。A relative path from the current directory of the C: drive.

重要

最後の 2 つのパスの違いにご注意ください。Note the difference between the last two paths. いずれも任意のボリューム指定子を指定しますが (いずれの場合も C:)、最後から 2 番目のパスが指定ボリュームのルートから始まるのに対し、最後のパスは指定ボリュームのルートから始まりません。Both specify the optional volume specifier (C: in both cases), but the first begins with the root of the specified volume, whereas the second does not. 結果として、最後から 2 番目のパスがドライブ C: のルート ディレクトリからの絶対パスであるのに対し、最後のパスはドライブ C: の現在のディレクトリからの相対パスになります。As result, the first is an absolute path from the root directory of drive C:, whereas the second is a relative path from the current directory of drive C:. 最後から 2 番目のパス形式を意図しているときに最後のパス形式を使用することが、Windows ファイル パス関連のバグの共通の源になっています。Use of the second form when the first is intended is a common source of bugs that involve Windows file paths.

IsPathFullyQualified メソッドを呼び出すことで、ファイルが完全修飾であるかどうかを判断できます。完全修飾の場合、パスが現在のディレクトリに依存せず、現在のディレクトリが変更されても完全修飾のパスは変わりません。You can determine whether a file path is fully qualified (that is, it the path is independent of the current directory and does not change when the current directory changes) by calling the IsPathFullyQualified method. そのようなパスには相対ディレクトリのセグメント (...) が含まれている可能性があり、解決後のパスが常に同じ場所を指す場合、完全修飾できることにご留意ください。Note that such a path can include relative directory segments (. and ..) and still be fully qualified if the resolved path always points to the same location.

次の例では、絶対パスと相対パスの違いを示します。The following example illustrates the difference between absolute and relative paths. ディレクトリ D:\FY2018\ が存在すること、この例を実行する前に、コマンド プロンプトから D:\ に現在のディレクトリを設定していないことを想定しています。It assumes that the directory D:\FY2018\ exists, and that you haven't set any curent directory for D:\ from the command prompt before running the example.

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

public class Example
{
   public static void Main(string[] args)
   {
      Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'");
      Console.WriteLine("Setting current directory to 'C:\\'");
      
      Directory.SetCurrentDirectory(@"C:\");
      string path = Path.GetFullPath(@"D:\FY2018");
      Console.WriteLine($"'D:\\FY2018' resolves to {path}");
      path = Path.GetFullPath(@"D:FY2018");
      Console.WriteLine($"'D:FY2018' resolves to {path}");

      Console.WriteLine("Setting current directory to 'D:\\Docs'");
      Directory.SetCurrentDirectory(@"D:\Docs");

      path = Path.GetFullPath(@"D:\FY2018");
      Console.WriteLine($"'D:\\FY2018' resolves to {path}");
      path = Path.GetFullPath(@"D:FY2018");

      // This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
      Console.WriteLine($"'D:FY2018' resolves to {path}");

      Console.WriteLine("Setting current directory to 'C:\\'");
      Directory.SetCurrentDirectory(@"C:\");

      path = Path.GetFullPath(@"D:\FY2018");
      Console.WriteLine($"'D:\\FY2018' resolves to {path}");

      // This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
      // the command prompt set the current directory before launch of our application, which
      // sets a hidden environment variable that is considered.
      path = Path.GetFullPath(@"D:FY2018");
      Console.WriteLine($"'D:FY2018' resolves to {path}");

      if (args.Length < 1)
      {
         Console.WriteLine(@"Launching again, after setting current directory to D:\FY2018");
         Uri currentExe = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute);
         string commandLine = $"/C cd D:\\FY2018 & \"{currentExe.LocalPath}\" stop";
         ProcessStartInfo psi = new ProcessStartInfo("cmd", commandLine); ;
         Process.Start(psi).WaitForExit();

         Console.WriteLine("Sub process returned:");
         path = Path.GetFullPath(@"D:\FY2018");
         Console.WriteLine($"'D:\\FY2018' resolves to {path}");
         path = Path.GetFullPath(@"D:FY2018");
         Console.WriteLine($"'D:FY2018' resolves to {path}");
      }
      Console.WriteLine("Press any key to continue... ");
      Console.ReadKey();
   }
}
// The example displays the following output:
//      Current directory is 'C:\Programs\file-paths'
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to d:\FY2018
//      Setting current directory to 'D:\Docs'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\Docs\FY2018
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to d:\FY2018
//      Launching again, after setting current directory to D:\FY2018
//      Sub process returned:
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to d:\FY2018
// The subprocess displays the following output:
//      Current directory is 'C:\'
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\FY2018\FY2018
//      Setting current directory to 'D:\Docs'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\Docs\FY2018
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\FY2018\FY2018
Imports System.Diagnostics
Imports System.IO
Imports System.Reflection

Public Module Example

   Public Sub Main(args() As String)
      Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'")
      Console.WriteLine("Setting current directory to 'C:\'")
      Directory.SetCurrentDirectory("C:\")
 
      Dim filePath As String = Path.GetFullPath("D:\FY2018")
      Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
      filePath = Path.GetFullPath("D:FY2018")
      Console.WriteLine($"'D:FY2018' resolves to {filePath}")

      Console.WriteLine("Setting current directory to 'D:\\Docs'")
      Directory.SetCurrentDirectory("D:\Docs")

      filePath = Path.GetFullPath("D:\FY2018")
      Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
      filePath = Path.GetFullPath("D:FY2018")

      ' This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
      Console.WriteLine($"'D:FY2018' resolves to {filePath}")

      Console.WriteLine("Setting current directory to 'C:\\'")
      Directory.SetCurrentDirectory("C:\")

      filePath = Path.GetFullPath("D:\FY2018")
      Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")

      ' This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
      ' the command prompt set the current directory before launch of our application, which
      ' sets a hidden environment variable that is considered.
      filePath = Path.GetFullPath("D:FY2018")
      Console.WriteLine($"'D:FY2018' resolves to {filePath}")

      If args.Length < 1 Then
         Console.WriteLine("Launching again, after setting current directory to D:\FY2018")
         Dim currentExe As New Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute)
         Dim commandLine As String = $"/C cd D:\FY2018 & ""{currentExe.LocalPath}"" stop"
         Dim psi As New ProcessStartInfo("cmd", commandLine) 
         Process.Start(psi).WaitForExit()

         Console.WriteLine("Sub process returned:")
         filePath = Path.GetFullPath("D:\FY2018")
         Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
         filePath = Path.GetFullPath("D:FY2018")
         Console.WriteLine($"'D:FY2018' resolves to {filePath}")
      End If
      Console.WriteLine("Press any key to continue... ")
      Console.ReadKey()
   End Sub
End Module
' The example displays the following output:
'      Current directory is 'C:\Programs\file-paths'
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to d:\FY2018
'      Setting current directory to 'D:\Docs'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\Docs\FY2018
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to d:\FY2018
'      Launching again, after setting current directory to D:\FY2018
'      Sub process returned:
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to d:\FY2018
' The subprocess displays the following output:
'      Current directory is 'C:\'
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\FY2018\FY2018
'      Setting current directory to 'D:\Docs'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\Docs\FY2018
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\FY2018\FY2018

UNC パスUNC paths

UNC (汎用命名規則) パスはネットワーク リソースへのアクセスに利用され、次の形式になっています。Universal naming convention (UNC) paths, which are used to access network resources, have the following format:

  • サーバーまたはホストの名前。先頭に \\ が付きます。A server or host name, which is prefaced by \\. サーバー名は、NetBIOS マシン名か IP/FQDN アドレス (IPv4 と v6 に対応) にすることができます。The server name can be a NetBIOS machine name or an IP/FQDN address (IPv4 as well as v6 are supported).
  • 共有名。ホスト名とは \ で区切られます。A share name, which is separated from the host name by \. サーバーと共有名を合わせてボリュームになります。Together, the server and share name make up the volume.
  • ディレクトリ名。A directory name. ディレクトリ区切り文字によって、入れ子になっているディレクトリ階層内でサブディレクトリが分割されます。The directory separator character separates subdirectories within the nested directory hierarchy.
  • 任意のファイル名。An optional filename. ディレクトリ区切り文字によって、ファイル パスとファイル名が分割されます。The directory separator character separates the file path and the filename.

UNC パスの例を次に示します。The following are some examples of UNC paths:

パスPath 説明Description
\\system07\C$\ system07 の C: のルート ディレクトリ。The root directory of the C: drive on system07.
\\Server2\Share\Test\Foo.txt \\Server2\Share ボリュームの Test ディレクトリの Foo.txt ファイル。The Foo.txt file in the Test directory of the \\Server2\Share volume.

UNC パスは常に完全修飾にする必要があります。UNC paths must always be fully qualified. 相対ディレクトリ セグメント (...) を含めることができますが、そのようなセグメントは完全修飾パスの一部にする必要があります。They can include relative directory segments (. and ..), but these must be part of a fully qualified path. 相対パスは、UNC パスをドライブ文字にマッピングする方法でのみ使用できます。You can use relative paths only by mapping a UNC path to a drive letter.

DOS デバイス パスDOS device paths

Windows オペレーティング システムには、ファイルを含む、すべてのリソースを指す統一オブジェクト モデルがあります。The Windows operating system has a unified object model that points to all resources, including files. これらのオブジェクト パスにはコンソール ウィンドウからアクセスできます。また、これらのオブジェクト パスは、DOS と UNC のレガシ パスがマッピングされているシンボリック リンクの特別なフォルダーを介して Win32 層に公開されます。These object paths are accessible from the console window and are exposed to the Win32 layer through a special folder of symbolic links that legacy DOS and UNC paths are mapped to. この特別なフォルダーには、DOS デバイス パス構文を介してアクセスできます。この構文は次のいずれかになります。This special folder is accessed via the DOS device path syntax, which is one of:

\\.\C:\Test\Foo.txt
\\?\C:\Test\Foo.txt

ドライブ文字でドライブを識別できるだけでなく、ボリューム GUID を使用してボリュームを識別できます。In addition to identifying a drive by its drive letter, you can identify a volume by using its volume GUID. 次のような形式になります。This takes the form:

\\.\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt \\?\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt

注意

DOS デバイス パス構文は、Windows で実行されている .NET 実装でサポートされます。 .NET Core は 1.1 以降、.NET Framework は 4.6.2 以降となります。DOS device path syntax is supported on .NET implementations running on Windows starting with .NET Core 1.1 and .NET Framework 4.6.2.

DOS デバイス パスは次の要素から構成されます。The DOS device path consists of the following components:

  • デバイス パス指定子 (\\.\\\?\)。この指定子により、DOS デバイス パスとしてパスが識別されます。The device path specifier (\\.\ or \\?\), which identifies the path as a DOS device path.

    注意

    \\?\ は、あらゆるバージョンの .NET Core とバージョン 4.6.2 以降の .NET Framework でサポートされます。The \\?\ is supported in all versions of .NET Core and in the .NET Framework starting with version 4.6.2.

  • "実際の" デバイス オブジェクトへのシンボリック リンク (ドライブ名の場合は C:、ボリュームの GUID の場合は Volume{b75e2c83-0000-0000-0000-602f00000000})。A symbolic link to the "real" device object (C: in the case of a drive name, or Volume{b75e2c83-0000-0000-0000-602f00000000} in the case of a volume GUID).

    デバイス パス指定子の後の最初のセグメントによって、ボリュームまたはドライブが識別されます。The first segment of the DOS device path after the device path specifier identifies the volume or drive. (たとえば、\\?\C:\\\.\BootPartition\)。(For example, \\?\C:\ and \\.\BootPartition\.)

    UNC のためのリンク (わかりやすいことに UNC) が呼び出されます。There is a specific link for UNCs that is called, not surprisingly, UNC. 次に例を示します。For example:

    \\.\UNC\Server\Share\Test\Foo.txt
    \\?\UNC\Server\Share\Test\Foo.txt

    デバイス UNC の場合、サーバー/共有の部分がボリュームになります。For device UNCs, the server/share portion forms the volume. たとえば、\\?\server1\e:\utilities\\filecomparer\ では、サーバー/共有部分は server1\utilities です。For example, in \\?\server1\e:\utilities\\filecomparer\, the server/share portion is server1\utilities. 相対ディレクトリ セグメントのある Path.GetFullPath(String, String) のようなメソッドを呼び出すとき、これは重要です。ボリュームを通り過ぎて移動することはできません。This is significant when calling a method such as Path.GetFullPath(String, String) with relative directory segments; it is never possible to navigate past the volume.

DOS デバイス パスは定義によって完全修飾されます。DOS device paths are fully qualified by definition. 相対ディレクトリ セグメント (...) は許可されません。Relative directory segments (. and ..) are not allowed. 現在のディレクトリが使用されることはありません。Current directories never enter into their usage.

例:同じファイルを参照する方法Example: Ways to refer to the same file

次の例では、System.IO 名前空間で API を使用するとき、あるファイルを参照する方法をいくつか示しています。The following example illustrates some of the ways in which you can refer to a file when using the APIs in the System.IO namespace. この例では FileInfo オブジェクトをインスタンス化し、その Name プロパティと Length プロパティを使用してファイルの名前と長さを表示します。The example instantiates a FileInfo object and uses its Name and Length properties to display the filename and the length of the file.

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string[] filenames = {
            @"c:\temp\test-file.txt",
            @"\\127.0.0.1\c$\temp\test-file.txt",
            @"\\LOCALHOST\c$\temp\test-file.txt",
            @"\\.\c:\temp\test-file.txt",
            @"\\?\c:\temp\test-file.txt",
            @"\\.\UNC\LOCALHOST\c$\temp\test-file.txt",
            @"\\127.0.0.1\c$\temp\test-file.txt" };

        foreach (var filename in filenames)
        {
            FileInfo fi = new FileInfo(filename);
            Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes");
        }
    }
}
// The example displays output like the following:
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
Imports System.IO

Module Program
    Sub Main()
        Dim filenames() As String = { 
                "c:\temp\test-file.txt", 
                "\\127.0.0.1\c$\temp\test-file.txt",
                "\\LOCALHOST\c$\temp\test-file.txt", 
                "\\.\c:\temp\test-file.txt",
                "\\?\c:\temp\test-file.txt",
                "\\.\UNC\LOCALHOST\c$\temp\test-file.txt",
                "\\127.0.0.1\c$\temp\test-file.txt" }

        For Each filename In filenames 
           Dim fi As New FileInfo(filename)   
           Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes")
        Next   
    End Sub
End Module

パスの正規化Path normalization

Windows API に渡されるパスはほとんどすべて正規化されます。Almost all paths passed to Windows APIs are normalized. 正規化の間、Windows では次の手順が実行されます。During normalization, Windows performs the following steps:

  • パスを識別します。Identifies the path.
  • 部分的に修飾された (相対) パスに現在のディレクトリが適用されます。Applies the current directory to partially qualified (relative) paths.
  • コンポーネントとディレクトリの区切り記号を正規化します。Canonicalizes component and directory separators.
  • 相対ディレクトリ コンポーネントを評価します (現在のディレクトリの場合は .、親ディレクトリの場合は ..)。Evaluates relative directory components (. for the current directory and .. for the parent directory).
  • 特定の文字をトリミングします。Trims certain characters.

この正規化は暗黙的に行われますが、Path.GetFullPath メソッドを呼び出すことで明示的に行うことができます。このメソッドは GetFullPathName() 関数の呼び出しをラップします。This normalization happens implicitly, but you can do it explicitly by calling the Path.GetFullPath method, which wraps a call to the GetFullPathName() function. Windows GetFullPathName() 関数 を P/Invoke で直接呼び出すこともできます。You can also call the Windows GetFullPathName() function directly using P/Invoke.

パスの識別Identifying the path

パス正規化の最初の手順は、パスの種類の識別です。The first step in path normalization is identifying the type of path. パスはいくつかあるカテゴリの 1 つに分類されます。Paths fall into one of a few categories:

  • パスはデバイス パスです。つまり、2 つの区切り記号で始まり、疑問符かピリオドが続きます (\\? または \\.)。They are device paths; that is, they begin with two separators and a question mark or period (\\? or \\.).
  • パスは UNC パスです。つまり、2 つの区切り記号で始まります。疑問符やピリオドは付きません。They are UNC paths; that is, they begin with two separators without a question mark or period.
  • パスは完全修飾 DOS パスです。つまり、ドライブ文字で始まり、ボリューム区切り記号、コンポーネント区切り記号が続きます (C:\)。They are fully qualified DOS paths; that is, they begin with a drive letter, a volume separator, and a component separator (C:\).
  • レガシ デバイスを示します (CONLPT1)。They designate a legacy device (CON, LPT1).
  • 現在のドライブのルートに相対です。つまり、1 つのコンポーネント区切り記号で始まります (\)。They are relative to the root of the current drive; that is, they begin with a single component separator (\).
  • 指定ドライブの現在のディレクトリに相対です。つまり、ドライブ文字で始まり、ボリューム区切り記号が続きますが、コンポーネント区切り記号はありません (C:)。They are relative to the current directory of a specified drive; that is, they begin with a drive letter, a volume separator, and no component separator (C:).
  • 現在のディレクトリに相対です。つまり、パスの最初の要素は問われません (temp\testfile.txt)。They are relative to the current directory; that is, they begin with anything else (temp\testfile.txt).

パスの種類によって、現在のディレクトリが何らかの方法で適用されるかどうかが決まります。The type of the path determines whether or not a current directory is applied in some way. パスの "ルート" も決定されます。It also determines what the "root" of the path is.

レガシ デバイスの取り扱いHandling legacy devices

パスが CONCOM1LPT1 など、レガシ DOS デバイスの場合、\\.\ を先頭に付けることでデバイス パスに変換した上で返されます。If the path is a legacy DOS device such as CON, COM1, or LPT1, it is converted into a device path by prepending \\.\ and returned.

Path.GetFullPath(String) メソッドは、レガシ デバイス名で始まるパスを常にレガシ デバイスとして解釈します。A path that begins with a legacy device name is always interpreted as a legacy device by the Path.GetFullPath(String) method. たとえば、CON.TXT の DOS デバイス パスは \\.\CON であり、COM1.TXT\file1.txt の DOS デバイス パスは \\.\COM1 です。For example, the DOS device path for CON.TXT is \\.\CON, and the DOS device path for COM1.TXT\file1.txt is \\.\COM1.

現在のディレクトリを適用するApplying the current directory

パスが完全修飾ではない場合、Windows はそのパスに現在のディレクトリを適用します。If a path isn't fully qualified, Windows applies the current directory to it. UNC とデバイス パスの場合、現在のディレクトリは適用されません。UNCs and device paths do not have the current directory applied. 区切り記号 (C:\) を使用するフル ドライブの場合も適用されません。Neither does a full drive with separator C:\.

パスが 1 つのコンポーネント区切り記号で始まる場合、現在のディレクトリからのドライブが適用されます。If the path starts with a single component separator, the drive from the current directory is applied. たとえば、ファイル パスが \utilities で、現在のディレクトリが C:\temp\ の場合、正規化の結果、C:\utilities になります。For example, if the file path is \utilities and the current directory is C:\temp\, normalization produces C:\utilities.

パスがドライブ文字で始まり、ボリューム区切り記号が続くが、コンポーネント区切り記号はない場合、指定ドライブのコマンド シェルから設定された最後の現在のディレクトリが適用されます。If the path starts with a drive letter, volume separator, and no component separator, the last current directory set from the command shell for the specified drive is applied. 最後の現在のディレクトリが設定されなかった場合、ドライブだけが適用されます。If the last current directory was not set, the drive alone is applied. たとえば、ファイル パスが D:sources、現在のディレクトリが C:\Documents\、ドライブ D: の最後の現在のディレクトリが D:\sources\ の場合、結果は D:\sources\sources になります。For example, if the file path is D:sources, the current directory is C:\Documents\, and the last current directory on drive D: was D:\sources\, the result is D:\sources\sources. プログラムやスクリプト ロジックでエラーが出るとき、一般的に、このような "ドライブ相対" パスが原因となります。These "drive relative" paths are a common source of program and script logic errors. 文字やコロンで始まるパスは相対ではないと考えることは明らかに正しくありません。Assuming that a path beginning with a letter and a colon isn't relative is obviously not correct.

パスが区切り記号以外で始まる場合、現在のドライブと現在のディレクトリが適用されます。If the path starts with something other than a separator, the current drive and current directory are applied. たとえば、パスが filecompare で、現在のディレクトリが C:\utilities\ の場合、結果は C:\utilities\filecompare\ です。For example, if the path is filecompare and the current directory is C:\utilities\, the result is C:\utilities\filecompare\.

重要

マルチスレッド アプリケーションの場合 (つまり、ほとんどのアプリケーションでは)、現在のディレクトリはプロセスごとの設定となるため、相対パスは危険です。Relative paths are dangerous in multithreaded applications (that is, most applications) because the current directory is a per-process setting. スレッドによって現在のディレクトリが変更されることは、いつでも起こりえることです。Any thread can change the current directory at any time. .NET Core 2.1 以降、Path.GetFullPath(String, String) メソッドを呼び出し、絶対パスに対して解決する場合、相対パスと基本パス (現在のディレクトリ) から絶対パスを取得できます。Starting with .NET Core 2.1, you can call the Path.GetFullPath(String, String) method to get an absolute path from a relative path and the base path (the current directory) that you want to resolve it against.

区切り記号の正規化Canonicalizing separators

フォワード スラッシュ (/) はすべて標準の Windows 区切り記号であるバック スラッシュ (\) に変換されます。All forward slashes (/) are converted into the standard Windows separator, the back slash (\). フォワード スラッシュがあるとき、最初の 2 つのスラッシュに続くスラッシュはすべて 1 つのスラッシュにまとめられます。If they are present, a series of slashes that follow the first two slashes are collapsed into a single slash.

相対コンポーネントを評価するEvaluating relative components

パスが処理されるとき、1 つか 2 つのピリオド (...) で構成されているコンポーネントやセグメントがあればそれは評価されます。As the path is processed, any components or segments that are composed of a single or a double period (. or ..) are evaluated:

  • ピリオドが 1 つの場合、現在のセグメントが削除されます。現在のディレクトリを参照するためです。For a single period, the current segment is removed, since it refers to the current directory.

  • ピリオドが 2 つの場合、現在のセグメントと親セグメントが削除されます。二重ピリオドは親ディレクトリを参照するためです。For a double period, the current segment and the parent segment are removed, since the double period refers to the parent directory.

    親ディレクトリは、それがパスのルートを通らない場合にのみ削除されます。Parent directories are only removed if they aren't past the root of the path. パスのルートは、パスの種類によって異なります。The root of the path depends on the type of path. DOS パスの場合はドライブ (C:\)、UNC の場合はサーバー/共有 (\\Server\Share)、デバイス パスの場合はデバイス パス プレフィックス (\\?\ または \\.\) となります。It is the drive (C:\) for DOS paths, the server/share for UNCs (\\Server\Share), and the device path prefix for device paths (\\?\ or \\.\).

文字のトリミングTrimming characters

一連の区切り記号や相対セグメントが削除されることを確認しましたが、それに加え、正規化の間、一部の追加文字が削除されます。Along with the runs of separators and relative segments removed earlier, some additional characters are removed during normalization:

  • セグメントがシングル ピリオドで終わる場合、そのピリオドは削除されます。If a segment ends in a single period, that period is removed. (シングルまたはダブル ピリオドのセグメントは前の手順で正規化されます。(A segment of a single or double period is normalized in the previous step. ピリオドが 3 つ以上のセグメントは正規化されません。実際には、有効なファイル/ディレクトリ名です。)A segment of three or more periods is not normalized and is actually a valid file/directory name.)

  • パスの終わりが区切り記号ではない場合、後ろに続くピリオドとスペース (U+0020) はすべて削除されます。If the path doesn't end in a separator, all trailing periods and spaces (U+0020) are removed. 最後のセグメントがシングルまたはダブル ピリオドの場合、上記の相対コンポーネント ルールに該当します。If the last segment is simply a single or double period, it falls under the relative components rule above.

    このルールによると、スペースの後に区切り記号を追加することで、スペースが後ろに続くディレクトリ名を作成できます。This rule means that you can create a directory name with a trailing space by adding a trailing separator after the space.

    重要

    スペースが後ろに続くディレクトリやファイル名は決して作成しないでください。You should never create a directory or filename with a trailing space. 後続スペースはディレクトリへのアクセスを困難または不可能にします。一般的に、名前に後続スペースが含まれるディレクトリやファイルを処理しようとすると、アプリケーションは失敗します。Trailing spaces can make it difficult or impossible to access a directory, and applications commonly fail when attempting to handle directories or files whose names include trailing spaces.

正規化の省略Skipping normalization

通常、Windows API に渡されるパスはすべて、GetFullPathName 関数に (効果的に) 渡され、正規化されます。Normally, any path passed to a Windows API is (effectively) passed to the GetFullPathName function and normalized. 重要な例外が 1 つあります。ピリオドではなく疑問符から始まるデバイス パスです。There is one important exception: a device path that begins with a question mark instead of a period. 厳密に \\?\ で始まらない限り (正規のバックスラッシュが使われていることに注目してください)、パスは正規化されます。Unless the path starts exactly with \\?\ (note the use of the canonical backslash), it is normalized.

正規化の省略が必要となる理由について考えてみます。Why would you want to skip normalization? 大きな理由が 3 つあります。There are three major reasons:

  1. 通常は利用できないが正当なパスにアクセスするため。To get access to paths that are normally unavailable but are legal. たとえば、hidden. というファイルまたはディレクトリには他の方法ではアクセスできません。A file or directory called hidden., for example, is impossible to access in any other way.

  2. 既に正規化している場合、省略することでパフォーマンスを改善するため。To improve performance by skipping normalization if you've already normalized.

  3. .NET Framework でのみ、259 文字を超えるパスの場合、パス長の MAX_PATH チェックを省略できます。On the .NET Framework only, to skip the MAX_PATH check for path length to allow for paths that are greater than 259 characters. 一部例外がありますが、ほとんどの API でこれが可能です。Most APIs allow this, with some exceptions.

注意

.NET Core は長いパスを暗黙的に処理し、MAX_PATH チェックを実行しません。.NET Core handles long paths implicitly and does not perform a MAX_PATH check. MAX_PATH は .NET Framework にのみ適用されます。The MAX_PATH check applies only to the .NET Framework.

正規化と MAX_PATH チェックの省略は、2 つのデバイス パス構文の間の唯一の違いです。それ以外、この 2 つは同じです。Skipping normalization and max path checks is the only difference between the two device path syntaxes; they are otherwise identical. 正規化の省略には注意してください。"普通の" アプリケーションでは処理できないパスが簡単に作られてしまいます。Be careful with skipping normalization, since you can easily create paths that are difficult for "normal" applications to deal with.

\\?\ で始まるパスは、GetFullPathName 関数に明示的に渡す場合、正規化されます。Paths that start with \\?\ are still normalized if you explicitly pass them to the GetFullPathName function.

MAX_PATH 文字を超えるパスは \\?\ なしで GetFullPathName に渡せることにご注意ください。Note that you can paths of more than MAX_PATH characters to GetFullPathName without \\?\. Windows で処理できる最大文字列サイズまで、任意の長さのパスがサポートされます。It supports arbitrary length paths up to the maximum string size that Windows can handle.

大文字/小文字の区別と Windows ファイル システムCase and the Windows file system

Windows を使っていないユーザーや開発者がとまどうことには、Windows ファイル システムではパス名とディレクトリ名で大文字と小文字が区別されないという特徴があります。A peculiarity of the Windows file system that non-Windows users and developers find confusing is that path and directory names are case-insensitive. つまり、ディレクトリ名とファイル名は、それが作成されたときの大文字/小文字の使い分けを反映します。That is, directory and file names reflect the casing of the strings used when they are created. たとえば、次のメソッドを呼び出すと、For example, the method call

Directory.Create("TeStDiReCtOrY");
Directory.Create("TeStDiReCtOrY")

TeStDiReCtOrY という名前のディレクトリが作成されます。creates a directory named TeStDiReCtOrY. ディレクトリやファイルの名前を変更し、大文字を小文字に変えるか、小文字を大文字に変えると、その名前変更時の大文字/小文字の使い方がディレクトリ名またはファイル名に反映されます。If you rename a directory or file to change its case, the directory or file name reflects the case of the string used when you rename it. たとえば、次のコードでは test.txt というファイルの名前が Test.txt に変更されます。For example, the following code renames a file named test.txt to Test.txt:

using System.IO;

class Example
{
   static void Main()
   {
      var fi = new FileInfo(@".\test.txt");
      fi.MoveTo(@".\Test.txt");
   }
}
Imports System.IO

Module Example
   Public Sub Main()
      Dim fi As New FileInfo(".\test.txt")
      fi.MoveTo(".\Test.txt")
   End Sub
End Module

しかしながら、ディレクトリ名とファイル名の比較では、大文字と小文字が区別されません。However, directory and file name comparisons are case-insensitive. "test.txt" という名前のファイルを検索すると、.NET ファイル システム API は比較で大文字/小文字の使い方を無視します。If you search for a file named "test.txt", .NET file system APIs ignore case in the comparison. Test.txt、TEST.TXT、test.TXT、大文字と小文字のその他すべての組み合わせが "test.txt" に一致します。Test.txt, TEST.TXT, test.TXT, and any other combination of upper- and lowercase letters will match "test.txt".