Formate von Dateipfaden unter Windows-Systemen

Die Member vieler Typen im System.IO-Namespace enthalten einen path-Parameter, mit dem Sie einen absoluten oder relativen Pfad zu einer Dateisystemressource festlegen können. Dieser Pfad wird dann an Windows-Dateisystem-APIs übergeben. In diesem Artikel werden die Formate für Dateipfade erläutert, die Sie unter Windows-Systemen verwenden können.

Herkömmliche DOS-Pfade

Ein standardmäßiger DOS-Pfad kann aus drei Komponenten bestehen:

  • Ein Volume oder Laufwerkbuchstabe gefolgt von dem entsprechenden Trennzeichen (:).
  • Ein Verzeichnisname. Das Verzeichnistrennzeichen trennt Unterverzeichnisse innerhalb der geschachtelten Verzeichnishierarchie.
  • Ein optionaler Dateiname. Das Verzeichnistrennzeichen trennt den Dateipfad und den Dateinamen.

Wenn alle drei Komponenten vorhanden sind, ist der Pfad absolut. Wenn kein Volume oder Laufwerkbuchstabe angegeben ist und der Name des Verzeichnisses mit dem Verzeichnistrennzeichen beginnt, ist der Pfad relativ zum Stamm des aktuellen Laufwerks. Andernfalls ist der Pfad relativ zum aktuellen Verzeichnis. In der folgenden Tabelle werden mögliche Verzeichnis- und Dateipfade veranschaulicht.

Pfad Beschreibung
C:\Documents\Newsletters\Summer2018.pdf Ein absoluter Dateipfad vom Stamm des Laufwerks C:
\Program Files\Custom Utilities\StringFinder.exe Ein relativer Pfad aus dem Stamm des aktuellen Laufwerks.
2018\January.xlsx Ein relativer Pfad zu einer Datei in einem Unterverzeichnis des aktuellen Verzeichnisses
..\Publications\TravelBrochure.pdf Ein relativer Pfad zu einer Datei in einem Verzeichnis vom aktuellen Verzeichnis aus.
C:\Projects\apilibrary\apilibrary.sln Ein absoluter Pfad zu einer Datei vom Stamm des Laufwerks C:
C:Projects\apilibrary\apilibrary.sln Ein relativer Pfad vom aktuellen Verzeichnis des Laufwerks C:

Wichtig

Beachten Sie den Unterschied zwischen den letzten beiden Pfaden. Beide geben den optionalen Volumebezeichner (in beiden Fällen C:) an. Der erste beginnt jedoch im Gegensatz zum zweiten mit dem Stamm des angegebenen Volumes. Daher ist der erste ein absoluter Pfad vom Stammverzeichnis des Laufwerks C:, während der zweite ein relativer Pfad vom aktuellen Verzeichnis des Laufwerks C: ist. Verwenden Sie das zweite Format, wenn das erste als Ursache für Fehler mit Windows-Dateipfaden bekannt ist.

Sie können ermitteln, ob ein Dateipfad absolut ist (d. h. der Pfad ist unabhängig vom aktuellen Verzeichnis und ändert sich nicht, wenn das aktuelle Verzeichnis geändert wird), indem Sie die Methode Path.IsPathFullyQualified aufrufen. Beachten Sie, dass ein solcher Pfad relative Verzeichnissegmente (. und ..) enthalten und weiterhin absolut sein kann, wenn der aufgelöste Pfad immer zum gleichen Speicherort führt.

Im folgenden Beispiel wird der Unterschied zwischen absoluten und relativen Pfaden veranschaulicht. Es wird vorausgesetzt, dass das Verzeichnis D:\FY2018\ vorhanden ist, und dass Sie kein aktuelles Verzeichnis für D:\ über die Eingabeaufforderung festgelegt haben, bevor Sie das Beispiel ausführen.

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-Pfade

UNC-Pfade (Universal Naming Convention), die für den Zugriff auf Netzwerkressourcen verwendet werden, weisen das folgende Format auf:

  • Ein Server- oder Hostname, dem \\ vorangestellt ist. Der Servername kann ein NetBIOS-Computername oder eine IP-/FQDN-Adresse sein (IPv4 und v6 werden unterstützt).
  • Ein Freigabename, der durch \ vom Hostnamen getrennt wird. Gemeinsam bilden der Server- und Freigabename das Volume.
  • Ein Verzeichnisname. Das Verzeichnistrennzeichen trennt Unterverzeichnisse innerhalb der geschachtelten Verzeichnishierarchie.
  • Ein optionaler Dateiname. Das Verzeichnistrennzeichen trennt den Dateipfad und den Dateinamen.

Im Folgenden werden einige Beispiele für UNC-Pfade aufgeführt:

Pfad Beschreibung
\\system07\C$\ Das Stammverzeichnis des Laufwerks C: auf system07
\\Server2\Share\Test\Foo.txt Die Datei Foo.txt im Testverzeichnis des Volumes \\Server2\Share

UNC-Pfade müssen immer absolut sein. Sie können relative Verzeichnissegmente (. und ..) enthalten, jedoch müssen diese Teil eines absoluten Pfads sein. Sie können relative Pfade nur verwenden, indem Sie einem Laufwerkbuchstaben einen UNC-Pfad zuordnen.

DOS-Gerätepfade

Das Windows-Betriebssystem verfügt über ein einheitliches Objektmodell, das auf alle Ressourcen, einschließlich Dateien, verweist. Sie können über das Konsolenfenster auf diese Objektpfade zugreifen. Diese werden über einen speziellen Ordner aus symbolischen Verknüpfungen, denen ältere DOS- und UNC-Pfade zugeordnet sind, für die Win32-Schicht zur Verfügung gestellt. Der Zugriff auf diesen speziellen Ordner erfolgt über die DOS-Gerätepfadsyntax, die einer der Folgenden entspricht:

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

Zusätzlich zum Identifizieren eines Laufwerks anhand des Laufwerkbuchstabens können Sie ein Volume mithilfe des Volume-GUID identifizieren. Dieser weist folgendes Format auf:

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

Hinweis

Die DOS-Gerätepfadsyntax wird beginnend mit .NET Core 1.1 und .NET Framework 4.6.2 von .NET-Implementierungen unterstützt, die unter Windows ausgeführt werden.

Der DOS-Gerätepfad besteht aus den folgenden Komponenten:

  • Der Gerätepfadbezeichner (\\.\ oder \\?\), der den Pfad als DOS-Gerätepfad identifiziert.

    Hinweis

    \\?\ wird in allen Versionen von .NET Core und .NET 5 und höher und ab Version 4.6.2 des .NET Framework unterstützt.

  • Ein symbolischer Link zum „echten“ Geräteobjekt (im Fall eines Laufwerknamens „C:“, im Fall eines Volume-GUID „Volume{b75e2c83-0000-0000-0000-602f00000000}“).

    Das erste Segment des DOS-Gerätepfads, nachdem der Gerätepfadbezeichner das Volume oder Laufwerk identifiziert. (Zum Beispiel \\?\C:\ und \\.\BootPartition\.)

    Es gibt eine spezifische Verknüpfung für UNC-Pfade mit dem unverwechselbaren Namen UNC. Zum Beispiel:

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

    Bei Geräte-UNCs bildet der Abschnitt „server/share“ das Volume. Zum Beispiel entspricht der Abschnitt „server/share“ in \\?\server1\utilities\\filecomparer\ dem Teil server1\utilities. Dies ist beim Aufrufen einer Methode mit relativen Verzeichnissegmenten wie Path.GetFullPath(String, String) wichtig. Es ist nicht möglich, weiter als zum Volume zu navigieren.

DOS-Gerätepfade sind definitionsgemäß vollständig qualifiziert und können nicht mit einem relativen Verzeichnissegment (. oder ..) beginnen. Aktuelle Verzeichnisse haben bei Verwendung von UNC-Pfaden keine Relevanz.

Beispiel: Möglichkeiten zum Verweisen auf dieselbe Datei

Im folgenden Beispiel werden einige Möglichkeiten zum Verweisen auf eine Datei veranschaulicht, wenn Sie die APIs des System.IO-Namespace verwenden. Im Beispiel wird ein FileInfo-Objekt instanziiert und dessen Name- und Length-Eigenschaften verwendet, um den Dateinamen und die Dateigröße anzuzeigen.

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

Pfadnormalisierung

Die meisten Pfade, die an Windows-APIs übergeben werden, werden normalisiert. Während der Normalisierung führt Windows die folgenden Schritte durch:

  • Der Pfad wird identifiziert.
  • Das aktuelle Verzeichnis wird auf relative Pfade angewandt.
  • Komponenten und Verzeichnistrennzeichen werden kanonisiert.
  • Relative Verzeichniskomponenten werden ausgewertet (. für das aktuelle Verzeichnis und .. für das übergeordnete Verzeichnis).
  • Bestimmte Zeichen werden gekürzt.

Diese Normalisierung erfolgt implizit, Sie können sie jedoch explizit ausführen, indem Sie die Methode Path.GetFullPath aufrufen, die einen Aufruf der GetFullPathName()-Funktion umschließt. Sie können die Windows-Funktion GetFullPathName() auch aufrufen, indem Sie „P/Invoke“ verwenden

Identifizieren des Pfads

Der erste Schritt der Pfadnormalisierung ist das Identifizieren des Pfadtyps. Pfade gehören zu einer von wenigen Kategorien:

  • Gerätepfade, d.h. sie beginnen mit zwei Trennzeichen und einem Fragezeichen oder einem Punkt (\\? oder \\.).
  • UNC-Pfade, d.h. sie beginnen mit zwei Trennzeichen ohne einem Fragezeichen oder Punkt.
  • Absolute DOS-Pfade, d.h. sie beginnen mit einem Laufwerkbuchstaben, einem Volumetrennzeichen und einem Komponententrennzeichen (C:\).
  • Sie legen ein Legacygerät fest (CON, LPT1).
  • Sie sind relativ zum Stamm des aktuellen Laufwerks, d.h. sie beginnen mit einem einzelnen Komponententrennzeichen (\).
  • Sie sind relativ zum aktuellen Verzeichnis eines angegebenen Laufwerks, d.h. sie beginnen mit einem Laufwerkbuchstaben, einem Volumetrennzeichen und keinem Komponententrennzeichen (C:).
  • Sie sind relativ zum aktuellen Verzeichnis, d.h. sie beginnen mit etwas anderem (temp\testfile.txt).

Der Typ des Pfad bestimmt, ob ein aktuelles Verzeichnis in irgendeiner Weise angewendet wird. Er bestimmt auch den „Stamm“ des Pfads.

Verarbeiten von Legacygeräten

Wenn der Pfad ein DOS-Legacygerät wie CON, COM1 oder LPT1 ist, wird er durch Voranstellen von \\.\ in einen Gerätepfad konvertiert und zurückgegeben.

Ein Pfad, der mit einem Legacygerätenamen beginnt, wird von der Methode Path.GetFullPath(String) immer als Legacygerät interpretiert. Beispielsweise ist der DOS-Gerätepfad für CON.TXT\\.\CON, und der DOS-Gerätepfad für COM1.TXT\file1.txt ist \\.\COM1.

Anwenden des aktuellen Verzeichnisses

Wenn ein Pfad nicht absolut ist, wendet Windows das aktuelle Verzeichnis an. Das aktuelle Verzeichnis wird nicht auf UNC- und Gerätepfade angewendet. Es wird auch nicht auf ein volles Laufwerk mit dem Trennzeichen C:\ angewendet.

Wenn der Pfad mit einem einzelnen Komponententrennzeichen beginnt, wird das Laufwerk des aktuellen Verzeichnisses angewendet. Wenn der Gerätepfad beispielsweise \utilities entspricht und das aktuelle Verzeichnis C:\temp\ ist, erzeugt die Normalisierung C:\utilities.

Wenn der Pfad mit einem Laufwerkbuchstaben, einem Volumetrennzeichen und keinem Komponententrennzeichen beginnt, wird das letzte über die Befehlsshell festgelegte aktuelle Verzeichnis für das angegebene Laufwerk angewendet. Wenn das letzte aktuelle Verzeichnis nicht festgelegt wurde, wird nur das Laufwerk angewendet. Wenn der Dateipfad beispielsweise D:sources entspricht, das aktuelle Verzeichnis C:\Documents\ ist, und das letzte aktuelle Verzeichnis auf Laufwerk „D:“ D:\sources\ war, ist das Ergebnis D:\sources\sources. Diese „zum Laufwerk relativen“ Pfade sind eine häufige Ursache für Logikfehler beim Programm und Skript. Die Annahme, dass ein Pfad, der mit einem Buchstaben und einem Doppelpunkt beginnt, nicht relativ ist, ist offensichtlich falsch.

Wenn der Pfad mit etwas anderem als einem Trennzeichen beginnt, werden das aktuelle Laufwerk und das aktuelle Verzeichnis angewendet. Wenn der Pfad beispielsweise filecompare entspricht, und das aktuelle Verzeichnis C:\utilities\ ist, ist das Ergebnis C:\utilities\filecompare\.

Wichtig

Relative Pfade sind in Multithreadanwendungen gefährlich (d.h. in den meisten Anwendungen), da das aktuelle Verzeichnis eine prozessspezifische Einstellung ist. Jeder Thread kann das aktuelle Verzeichnis jederzeit ändern. Beginnend mit .NET Core 2.1 können Sie die Methode Path.GetFullPath(String, String) aufrufen, um einen absoluten Pfad von einem relativen Pfad und dem Basispfad (dem aktuellen Verzeichnis) abzurufen, gegen die Sie auflösen möchten.

Umwandeln von Trennzeichen in eine kanonische Form

Alle führenden Schrägstriche (/) werden in das standardmäßige Trennzeichen von Windows konvertiert, den umgekehrten Schrägstrich (\). Wenn sie vorhanden sind, wird eine Reihe von Schrägstrichen, die auf die ersten zwei Schrägstriche folgt, auf einen einzelnen Schrägstrich reduziert.

Auswerten relativer Komponenten

Während der Pfad verarbeitet wird, werden alle Komponenten oder Segmente ausgewertet, die aus einem oder zwei Punkten (. oder ..) bestehen:

  • Bei einzelnen Punkten wird das aktuelle Segment entfernt, da es auf das aktuelle Verzeichnis verweist.

  • Bei zwei Punkten werden das aktuelle Segment und das übergeordnete Segment entfernt, da die zwei Punkte auf das übergeordnetes Verzeichnis verweisen.

    Übergeordnete Verzeichnisse werden nur entfernt, wenn sie sich nicht nach dem Stamm des Pfads befinden. Der Stamm des Pfads ist abhängig von der Art des Pfads. Für DOS-Pfade ist es das Laufwerk (C:\), für UNC-Pfade ist es „server/share“ (\\Server\Share) und für Gerätepfade ist es das Gerätepfadpräfix (\\?\ oder \\.\).

Kürzen von Zeichen

Zusätzlich zu den Ausführungen von Trennzeichen und Segmenten, die weiter oben entfernt wurden, werden einige zusätzliche Zeichen während der Normalisierung entfernt:

  • Wenn ein Segment mit einem einzelnen Punkt endet, wird der Punkt entfernt. (Ein Segment aus ein oder zwei Punkten wird im vorherigen Schritt normalisiert. Ein Segment aus mindestens drei Punkten wird nicht normalisiert und ist tatsächlich ein gültiger Datei- oder Verzeichnisname.)

  • Wenn der Pfad nicht mit einem Trennzeichen endet, werden alle nachfolgenden Punkte und Leerzeichen (U+0020) entfernt. Wenn das letzte Segment nur aus einem oder zwei Punkten besteht, unterliegt es den oben genannten Regeln für relative Komponenten.

    Diese Regel bedeutet, dass Sie einen Verzeichnisnamen mit einem nachfolgendem Leerzeichen erstellen können, indem Sie nach dem Leerzeichen ein Trennzeichen hinzufügen.

    Wichtig

    Sie sollten nie Verzeichnisse oder Dateinamen mit nachstehenden Leerzeichen erstellen. Nachstehende Leerzeichen machen es schwer, wenn nicht sogar unmöglich, auf ein Verzeichnis zuzugreifen. Außerdem treten häufig Fehler auf, wenn Anwendungen versuchen, Verzeichnisse oder Dateien zu verarbeiten, deren Namen nachstehende Leerzeichen enthalten.

Überspringen der Normalisierung

Normalerweise werden alle Pfade, die an die Windows-API übergeben werden, effektiv an die GetFullPathName-Funktion übergeben und normalisiert. Es gibt eine wichtige Ausnahme: ein Gerätepfad, der mit einem Fragezeichen statt einem Punkt beginnt. Sofern der Pfad nicht mit \\?\ beginnt (beachten Sie die Verwendung des kanonischen umgekehrten Schrägstrichs), ist er normalisiert.

Warum kann es sinnvoll sein, die Normalisierung zu überspringen? Es gibt drei wichtige Gründe:

  1. Um Zugriff auf Pfade zu erhalten, die zwar zulässig sind, aber normalerweise nicht verfügbar. Beispielsweise ist es auf keine andere Weise möglich, Zugriff auf eine Datei oder ein Verzeichnis namens hidden. zu erhalten.

  2. Zur Verbesserung der Leistung, wenn Sie die Normalisierung bereits durchgeführt haben.

  3. Zum Überspringen der MAX_PATH-Überprüfung der Pfadlänge, um Pfade mit mehr als 259 Zeichen zu ermöglichen (nur im .NET Framework). Abgesehen von einigen Ausnahmen ist dies mit den meisten APIs möglich.

Hinweis

In .NET Core und .NET 5 und höher werden lange Pfade implizit verarbeitet, und es wird keine MAX_PATH-Überprüfung durchgeführt. Die MAX_PATH-Überprüfung gilt nur für das .NET Framework.

Das Überspringen der Normalisierung und MAX_PATH-Überprüfungen ist der einzige Unterschied zwischen den zwei Gerätepfadsyntaxen. Andernfalls sind sie identisch. Beachten Sie, dass Sie durch Überspringen der Normalisierung Pfade erstellen können, die für „normale“ Anwendungen schwer zu verarbeiten sind.

Pfade, die mit \\?\ beginnen, werden weiterhin normalisiert, wenn Sie sie explizit an die GetFullPathName-Funktion übergeben.

Sie können Pfade mit mehr Zeichen als MAX_PATH übergeben, um GetFullPathName ohne \\?\ abzurufen. Beliebige Pfadlängen werden bis zur maximalen Länge von Zeichenfolgen unterstützt, die Windows verarbeiten kann.

Groß-/Kleinbuchstaben im Windows-Dateisystem

Dass Pfad- und Verzeichnisnamen die Groß-/Kleinschreibung ignorieren, ist eine Besonderheit vom Windows-Dateisystem, was viele Benutzer und Entwickler verwirrt, die Windows nicht verwenden. Das bedeutet, dass Verzeichnis- und Dateinamen die Schreibweise der Zeichenfolgen darstellen, mit der sie erstellt wurden. Zum Beispiel erstellt der Methodenaufruf

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

ein Verzeichnis namens TeStDiReCtOrY. Wenn Sie ein Verzeichnis oder eine Datei umbenennen, um die Groß-/Kleinschreibung zu ändern, wird die entsprechende Zeichenfolge angezeigt, die Sie beim Umbenennen eingegeben haben. Der folgende Code benennt beispielweise eine Datei namens „test.txt“ in „Test.txt“ um:

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

Allerdings berücksichtigen Vergleiche von Verzeichnis- und Dateinamen die Groß-/Kleinschreibung nicht. Wenn Sie nach einer Datei namens „test.txt“ suchen, ignorieren .NET-Dateisystem-APIs die Groß-/Kleinschreibung für Vergleiche. „Test.txt“, „TEST.TXT“, „test.TXT“ und beliebige andere Kombinationen aus Groß- und Kleinbuchstaben entsprechen „test.txt“.