Partilhar via


Formatos de caminho de arquivo em sistemas Windows

Os membros de muitos dos tipos no System.IO namespace incluem um path parâmetro que permite especificar um caminho absoluto ou relativo para um recurso do sistema de arquivos. Esse caminho é então passado para APIs do sistema de arquivos do Windows. Este tópico discute os formatos para caminhos de arquivo que você pode usar em sistemas Windows.

Caminhos DOS tradicionais

Um caminho DOS padrão pode consistir em três componentes:

  • Um volume ou letra de unidade seguida pelo separador de volume (:).
  • Um nome de diretório. O caractere separador de diretório separa subdiretórios dentro da hierarquia de diretório aninhada.
  • Um nome de arquivo opcional. O caractere separador de diretório separa o caminho do arquivo e o nome do arquivo.

Se todos os três componentes estiverem presentes, o caminho será absoluto. Se nenhum volume ou letra de unidade for especificado e o nome do diretório começar com o caractere separador de diretório, o caminho será relativo a partir da raiz da unidade atual. Caso contrário, o caminho é relativo ao diretório atual. A tabela a seguir mostra alguns caminhos possíveis de diretório e arquivo.

Caminho Description
C:\Documents\Newsletters\Summer2018.pdf Um caminho de arquivo absoluto da raiz da unidade C:.
\Program Files\Custom Utilities\StringFinder.exe Um caminho relativo da raiz da unidade atual.
2018\January.xlsx Um caminho relativo para um arquivo em um subdiretório do diretório atual.
..\Publications\TravelBrochure.pdf Um caminho relativo para um arquivo em um diretório a partir do diretório atual.
C:\Projects\apilibrary\apilibrary.sln Um caminho absoluto para um arquivo a partir da raiz da unidade C:.
C:Projects\apilibrary\apilibrary.sln Um caminho relativo do diretório atual da C: unidade.

Importante

Observe a diferença entre os dois últimos caminhos. Ambos especificam o especificador de volume opcional (C: em ambos os casos), mas o primeiro começa com a raiz do volume especificado, enquanto o segundo não. Como resultado, o primeiro é um caminho absoluto do diretório raiz da unidade C:, enquanto o segundo é um caminho relativo do diretório atual da unidade C:. O uso do segundo formulário quando o primeiro é pretendido é uma fonte comum de bugs que envolvem caminhos de arquivo do Windows.

Você pode determinar se um caminho de arquivo é totalmente qualificado (ou seja, se o caminho é independente do diretório atual e não muda quando o diretório atual é alterado) chamando o Path.IsPathFullyQualified método. Observe que esse caminho pode incluir segmentos de diretório relativos (. e ..) e ainda ser totalmente qualificado se o caminho resolvido sempre apontar para o mesmo local.

O exemplo a seguir ilustra a diferença entre caminhos absolutos e relativos. Ele pressupõe que o diretório D:\FY2018\ existe e que você não definiu nenhum diretório atual para D:\ a partir do prompt de comando antes de executar o exemplo.

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

public class Example2
{
    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(Assembly.GetExecutingAssembly().Location, UriKind.Absolute);
            string commandLine = $"/C cd D:\\FY2018 & \"{currentExe.LocalPath}\" stop";
            ProcessStartInfo psi = new("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.IO
Imports System.Reflection

Public Module Example2

    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

Caminhos UNC

Os caminhos UNC (convenção universal de nomenclatura), que são usados para acessar recursos de rede, têm o seguinte formato:

  • Um nome de servidor ou host, que é precedido por \\. O nome do servidor pode ser um nome de máquina NetBIOS ou um endereço IP/FQDN (IPv4 e v6 são suportados).
  • Um nome de compartilhamento, que é separado do nome do host por \. Juntos, o servidor e o nome do compartilhamento compõem o volume.
  • Um nome de diretório. O caractere separador de diretório separa subdiretórios dentro da hierarquia de diretório aninhada.
  • Um nome de arquivo opcional. O caractere separador de diretório separa o caminho do arquivo e o nome do arquivo.

Seguem-se alguns exemplos de percursos UNC:

Caminho Description
\\system07\C$\ O diretório raiz da C: unidade em system07.
\\Server2\Share\Test\Foo.txt O Foo.txt arquivo no diretório Test do \\Server2\Share volume.

Os caminhos UNC devem ser sempre totalmente qualificados. Eles podem incluir segmentos de diretório relativos (. e ..), mas eles devem fazer parte de um caminho totalmente qualificado. Você pode usar caminhos relativos somente mapeando um caminho UNC para uma letra de unidade.

Caminhos de dispositivo DOS

O sistema operacional Windows tem um modelo de objeto unificado que aponta para todos os recursos, incluindo arquivos. Esses caminhos de objeto são acessíveis a partir da janela do console e são expostos à camada Win32 por meio de uma pasta especial de links simbólicos para os quais os caminhos DOS e UNC herdados são mapeados. Esta pasta especial é acessada através da sintaxe do caminho do dispositivo DOS, que é uma das:

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

Além de identificar uma unidade por sua letra de unidade, você pode identificar um volume usando seu GUID de volume. Isto assume a forma:

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

Nota

A sintaxe do caminho do dispositivo DOS é suportada em implementações .NET executadas no Windows a partir do .NET Core 1.1 e do .NET Framework 4.6.2.

O caminho do dispositivo DOS consiste nos seguintes componentes:

  • O especificador de caminho de dispositivo (\\.\ ou \\?\), que identifica o caminho como um caminho de dispositivo DOS.

    Nota

    O \\?\ é suportado em todas as versões do .NET Core e .NET 5+ e no .NET Framework a partir da versão 4.6.2.

  • Um link simbólico para o objeto de dispositivo "real" (C: no caso de um nome de unidade, ou Volume{b75e2c83-0000-0000-0000-602f00000000} no caso de um GUID de volume).

    O primeiro segmento do caminho do dispositivo DOS após o especificador de caminho do dispositivo identifica o volume ou a unidade. (Por exemplo, \\?\C:\ e \\.\BootPartition\.)

    Existe uma ligação específica para as UNC que se chama, não surpreendentemente, UNC. Por exemplo:

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

    Para UNCs de dispositivo, a parte servidor/compartilhamento forma o volume. Por exemplo, no \\?\server1\utilities\\filecomparer\, a parte servidor/compartilhamento é server1\utilities. Isso é significativo ao chamar um método, como Path.GetFullPath(String, String) com segmentos de diretório relativos, nunca é possível navegar além do volume.

Os caminhos de dispositivo DOS são totalmente qualificados por definição e não podem começar com um segmento de diretório relativo (. ou ..). Os diretórios atuais nunca entram em seu uso.

Exemplo: Formas de fazer referência ao mesmo ficheiro

O exemplo a seguir ilustra algumas das maneiras pelas quais você pode fazer referência a um arquivo ao usar as APIs no System.IO namespace. O exemplo instancia um FileInfo objeto e usa suas Name propriedades e Length para exibir o nome do arquivo e o comprimento do arquivo.

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" };

        foreach (string filename in filenames)
        {
            FileInfo fi = new(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
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"}

        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

Normalização de caminho

Quase todos os caminhos passados para APIs do Windows são normalizados. Durante a normalização, o Windows executa as seguintes etapas:

  • Identifica o caminho.
  • Aplica o diretório atual a caminhos parcialmente qualificados (relativos).
  • Canonicaliza separadores de componentes e diretórios.
  • Avalia os componentes relativos do diretório (. para o diretório atual e .. para o diretório pai).
  • Corta determinados caracteres.

Essa normalização acontece implicitamente, mas você pode fazê-lo explicitamente chamando o Path.GetFullPath método, que encapsula uma chamada para a função GetFullPathName(). Você também pode chamar a função GetFullPathName() do Windows diretamente usando P/Invoke.

Identificar o caminho

O primeiro passo na normalização do caminho é identificar o tipo de caminho. Os caminhos se enquadram em uma das poucas categorias:

  • São caminhos de dispositivos; ou seja, começam com dois separadores e um ponto de interrogação ou ponto (\\? ou \\.).
  • São caminhos UNC; ou seja, começam com dois separadores sem ponto de interrogação ou ponto.
  • São caminhos DOS totalmente qualificados; ou seja, eles começam com uma letra de unidade, um separador de volume e um separador de componente (C:\).
  • Eles designam um dispositivo herdado (CON, LPT1).
  • Eles são relativos à raiz da unidade atual; ou seja, começam com um único separador de componente (\).
  • Eles são relativos ao diretório atual de uma unidade especificada; ou seja, eles começam com uma letra de unidade, um separador de volume e nenhum separador de componente (C:).
  • Eles são relativos ao diretório atual; ou seja, começam com qualquer outra coisa (temp\testfile.txt).

O tipo do caminho determina se um diretório atual é ou não aplicado de alguma forma. Também determina qual é a "raiz" do caminho.

Lidar com dispositivos legados

Se o caminho for um dispositivo DOS herdado, como CON, COM1ou LPT1, ele será convertido em um caminho de dispositivo por pré-pendente \\.\ e retornado.

Um caminho que começa com um nome de dispositivo herdado é sempre interpretado como um dispositivo herdado pelo Path.GetFullPath(String) método. Por exemplo, o caminho do dispositivo DOS para CON.TXT é \\.\CON, e o caminho do dispositivo DOS para COM1.TXT\file1.txt é \\.\COM1.

Aplicar o diretório atual

Se um caminho não estiver totalmente qualificado, o Windows aplicará o diretório atual a ele. UNCs e caminhos de dispositivo não têm o diretório atual aplicado. Nem uma unidade completa com separador C:\.

Se o caminho começar com um único separador de componente, a unidade do diretório atual será aplicada. Por exemplo, se o caminho do arquivo for \utilities e o diretório atual for C:\temp\, a normalização produzirá C:\utilities.

Se o caminho começar com uma letra de unidade, separador de volume e nenhum separador de componente, o último diretório atual definido do shell de comando para a unidade especificada será aplicado. Se o último diretório atual não foi definido, a unidade sozinha é aplicada. Por exemplo, se o caminho do arquivo for D:sources, o diretório atual for C:\Documents\, e o último diretório atual na unidade D: was D:\sources\, o resultado será D:\sources\sources. Esses caminhos "relativos à unidade" são uma fonte comum de erros de lógica de programa e script. Supor que um caminho que começa com uma letra e dois pontos não é relativo obviamente não é correto.

Se o caminho começar com algo diferente de um separador, a unidade atual e o diretório atual serão aplicados. Por exemplo, se o caminho for filecompare e o diretório atual for C:\utilities\, o resultado será C:\utilities\filecompare\.

Importante

Os caminhos relativos são perigosos em aplicativos multithreaded (ou seja, a maioria dos aplicativos) porque o diretório atual é uma configuração por processo. Qualquer thread pode alterar o diretório atual a qualquer momento. A partir do .NET Core 2.1, você pode chamar o Path.GetFullPath(String, String) método para obter um caminho absoluto de um caminho relativo e o caminho base (o diretório atual) contra o qual deseja resolvê-lo.

Separadores canonicalizados

Todas as barras (/) são convertidas no separador padrão do Windows, a barra invertida (\). Se estiverem presentes, uma série de barras que se seguem às duas primeiras barras são colapsadas numa única barra.

Avaliar componentes relativos

À medida que o caminho é processado, quaisquer componentes ou segmentos que são compostos por um único ou um período duplo (. ou ..) são avaliados:

  • Por um único período, o segmento atual é removido, uma vez que se refere ao diretório atual.

  • Para um período duplo, o segmento atual e o segmento pai são removidos, uma vez que o período duplo refere-se ao diretório pai.

    Os diretórios pai só são removidos se não passarem da raiz do caminho. A raiz do caminho depende do tipo de caminho. É a unidade (C:\) para caminhos DOS, o servidor/compartilhamento para UNCs (\\Server\Share) e o prefixo de caminho de dispositivo para caminhos de dispositivo (\\?\ ou \\.\).

Cortar caracteres

Juntamente com as execuções de separadores e segmentos relativos removidos anteriormente, alguns caracteres adicionais são removidos durante a normalização:

  • Se um segmento terminar em um único período, esse período será removido. (Um segmento de um período único ou duplo é normalizado na etapa anterior. Um segmento de três ou mais períodos não é normalizado e é, na verdade, um nome de arquivo/diretório válido.)

  • Se o caminho não terminar em um separador, todos os períodos e espaços à direita (U+0020) serão removidos. Se o último segmento for simplesmente um período único ou duplo, ele se enquadra na regra de componentes relativos acima.

    Esta regra significa que você pode criar um nome de diretório com um espaço à direita adicionando um separador à direita após o espaço.

    Importante

    Você nunca deve criar um diretório ou nome de arquivo com um espaço à direita. Os espaços à direita podem dificultar ou impossibilitar o acesso a um diretório, e os aplicativos geralmente falham ao tentar manipular diretórios ou arquivos cujos nomes incluem espaços à direita.

Ignorar normalização

Normalmente, qualquer caminho passado para uma API do Windows é (efetivamente) passado para a função GetFullPathName e normalizado. Há uma exceção importante: um caminho de dispositivo que começa com um ponto de interrogação em vez de um ponto. A menos que o caminho comece exatamente com \\?\ (observe o uso da barra invertida canônica), ele é normalizado.

Por que você gostaria de pular a normalização? Existem três razões principais:

  1. Para ter acesso a caminhos que normalmente não estão disponíveis, mas são legais. Um arquivo ou diretório chamado hidden., por exemplo, é impossível de acessar de qualquer outra forma.

  2. Para melhorar o desempenho ignorando a normalização se você já tiver normalizado.

  3. Somente no .NET Framework, ignore a verificação do comprimento do MAX_PATH caminho para permitir caminhos maiores que 259 caracteres. A maioria das APIs permite isso, com algumas exceções.

Nota

O .NET Core e o .NET 5+ lidam com caminhos longos implicitamente e não executam uma MAX_PATH verificação. A MAX_PATH verificação aplica-se apenas ao .NET Framework.

Ignorar a normalização e as verificações de caminho máximo é a única diferença entre as duas sintaxes de caminho do dispositivo; de outro modo, são idênticos. Tenha cuidado ao pular a normalização, pois você pode facilmente criar caminhos difíceis para aplicativos "normais" lidarem.

Os caminhos que começam com \\?\ ainda são normalizados se você passá-los explicitamente para a função GetFullPathName.

Você pode passar caminhos de mais de MAX_PATH caracteres para GetFullPathName sem \\?\. Ele suporta caminhos de comprimento arbitrário até o tamanho máximo de cadeia de caracteres que o Windows pode manipular.

Case e o sistema de arquivos do Windows

Uma peculiaridade do sistema de arquivos do Windows que os usuários e desenvolvedores que não são do Windows acham confuso é que os nomes de caminho e diretório não diferenciam maiúsculas de minúsculas. Ou seja, os nomes de diretório e arquivo refletem o invólucro das cadeias de caracteres usadas quando são criadas. Por exemplo, a chamada de método

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

cria um diretório chamado TeStDiReCtOrY. Se você renomear um diretório ou arquivo para alterar suas maiúsculas e minúsculas, o diretório ou nome do arquivo refletirá o caso da cadeia de caracteres usada quando você o renomeia. Por exemplo, o código a seguir renomeia um arquivo chamado test.txt para Test.txt:

using System.IO;

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

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

No entanto, as comparações de diretório e nome de arquivo não diferenciam maiúsculas de minúsculas. Se você procurar um arquivo chamado "test.txt", as APIs do sistema de arquivos .NET ignoram maiúsculas e minúsculas na comparação. "Test.txt", "TEST.TXT", "test.TXT" e qualquer outra combinação de letras maiúsculas e minúsculas corresponderão a "test.txt".