2008-11-28 2 views
26

Je veux faire exactement la même chose que dans this question:Obtenir nom du fichier (avec boîtier approprié) sous Windows avec .NET

système de fichiers

Windows est insensible à la casse. Comment, étant donné un nom de fichier/dossier (par exemple "somefile"), je reçois le nom réel de ce fichier/dossier (par exemple, il devrait retourner "SomeFile" si Explorer l'affiche ainsi)?

Mais je dois le faire dans .NET et je veux le chemin complet (D:/Temp/Foobar.xml et pas seulement Foobar.xml).

Je vois que FullName sur la classe FileInfo ne fait pas l'affaire.

Répondre

21

Il me semble que depuis NTFS est insensible à la casse, il acceptera toujours votre entrée correctement, peu importe si le nom est classé correctement. La seule façon d'obtenir le nom de chemin correct semble trouver le fichier comme suggéré par John Sibly.

J'ai créé une méthode qui prendra un chemin (dossier ou un fichier) et le retour de celui-ci Carrossé correctement (pour le chemin complet):

public static string GetExactPathName(string pathName) 
    { 
     if (!(File.Exists(pathName) || Directory.Exists(pathName))) 
      return pathName; 

     var di = new DirectoryInfo(pathName); 

     if (di.Parent != null) { 
      return Path.Combine(
       GetExactPathName(di.Parent.FullName), 
       di.Parent.GetFileSystemInfos(di.Name)[0].Name); 
     } else { 
      return di.Name.ToUpper(); 
     } 
    } 

Voici quelques cas de test qui ont travaillé sur ma machine :

static void Main(string[] args) 
    { 
     string file1 = @"c:\documents and settings\administrator\ntuser.dat"; 
     string file2 = @"c:\pagefile.sys"; 
     string file3 = @"c:\windows\system32\cmd.exe"; 
     string file4 = @"c:\program files\common files"; 
     string file5 = @"ddd"; 

     Console.WriteLine(GetExactPathName(file1)); 
     Console.WriteLine(GetExactPathName(file2)); 
     Console.WriteLine(GetExactPathName(file3)); 
     Console.WriteLine(GetExactPathName(file4)); 
     Console.WriteLine(GetExactPathName(file5)); 

     Console.ReadLine(); 
    } 

La méthode renvoie la valeur fournie si le fichier n'existe pas.

Il pourrait y avoir des méthodes plus rapides (ceci utilise la récursivité) mais je ne suis pas sûr s'il y a des manières évidentes de le faire.

+3

NTFS est définitivement cas * sensible *. C'est l'API Windows qui est incohérente à ce sujet. –

+1

Si vous remplacez 'return di.Name.ToUpper();' par 'return di.FullName.ToUpper();' (FullName), il fonctionnera même avec les chemins UNC. :-) – ygoe

+0

J'ai aimé cette réponse, mais je l'ai adaptée avec une méthode TryGetExactPath qui prend en charge les chemins UNC, me dit si le chemin n'existe pas et utilise l'itération au lieu de la récursivité pour réduire les appels à Path.Combine. –

-3

Avez-vous essayé la classe DirectoryInfo et Path, ils pourraient faire l'affaire. (Je ne l'ai pas essayé moi-même)

0

Problème intéressant.

Une façon de le faire est de "trouver" un fichier basé sur le nom insensible à la casse, puis regardez la propriété FileInfo.FullName. J'ai testé cela en utilisant la fonction suivante et cela donne le résultat requis.

static string GetCaseSensitiveFileName(string filePath) 
{ 
    string caseSensitiveFilePath = null; 

    DirectoryInfo dirInfo = new DirectoryInfo(Path.GetDirectoryName(filePath)); 
    FileInfo[] files = dirInfo.GetFiles(Path.GetFileName(filePath)); 
    if (files.Length > 0) 
    { 
     caseSensitiveFilePath = files[0].FullName; 
    } 

    return caseSensitiveFilePath; 
} 

Vous devez être un peu prudent - si vous avez deux fichiers noms callled comme file.xml et File.xml il serait alors seulement retourner le premier.

+0

La question initiale déjà dit que regarder FileInfo.FullName ne fournit pas les informations requises. –

+1

C'est correct - si vous utilisez simplement la classe FileInfo, c'est le cas. Mais si vous trouvez à nouveau le fichier en utilisant DirectoryInfo et GetFiles (passant dans le chemin d'accès complet), si vous regardez le FullPath, vous pouvez sauvegarder les informations requises. J'ai testé la fonction que j'ai posté et cela fonctionne. –

+1

Vous avez raison, cela fonctionne et renvoie les informations correctes. –

1

Je pense que la seule façon de faire cela est d'utiliser la même API Win32, à savoir la méthode SHGetFileInfo, mentionnée dans la réponse acceptée pour la question que vous référencez. Pour ce faire, vous devrez utiliser des appels interop p/invoke. Jetez un oeil à pinvoke.net pour un exemple de la façon de le faire et quelles structures supplémentaires vous aurez besoin.

0

Il semble que la meilleure façon est de parcourir tous les dossiers dans le chemin et obtenir leurs propres casquettes:

Public Function gfnProperPath(ByVal sPath As String) As String 
    If Not IO.File.Exists(sPath) AndAlso Not IO.Directory.Exists(sPath) Then Return sPath 
    Dim sarSplitPath() As String = sPath.Split("\") 
    Dim sAddPath As String = sarSplitPath(0).ToUpper & "\" 
    For i = 1 To sarSplitPath.Length - 1 
     sPath = sAddPath & "\" & sarSplitPath(i) 
     If IO.File.Exists(sPath) Then 
      Return IO.Directory.GetFiles(sAddPath, sarSplitPath(i), IO.SearchOption.TopDirectoryOnly)(0) 
     ElseIf IO.Directory.Exists(sPath) Then 
      sAddPath = IO.Directory.GetDirectories(sAddPath, sarSplitPath(i), IO.SearchOption.TopDirectoryOnly)(0) 
     End If 
    Next 
    Return sPath 
End Function 
3

Ma deuxième réponse ici avec une méthode non récursive. Il accepte les fichiers et les répertoires.
Cette fois traduit de VB à C#:

private string fnRealCAPS(string sDirOrFile) 
{ 
    string sTmp = ""; 
    foreach (string sPth in sDirOrFile.Split("\\")) { 
     if (string.IsNullOrEmpty(sTmp)) { 
      sTmp = sPth + "\\"; 
      continue; 
     } 
     sTmp = System.IO.Directory.GetFileSystemEntries(sTmp, sPth)[0]; 
    } 
    return sTmp; 
}
+1

Upvoted, mais la notation hongroise m'a fait pleurer :-) – Konamiman

4

Inspiré par la réponse d'Ivan, voici une méthode qui gère également boîtier de lettre de lecteur ainsi:

public string FixFilePathCasing(string filePath) 
{ 
    string fullFilePath = Path.GetFullPath(filePath); 

    string fixedPath = ""; 
    foreach(string token in fullFilePath.Split('\\')) 
    { 
     //first token should be drive token 
     if(fixedPath == "") 
     { 
      //fix drive casing 
      string drive = string.Concat(token, "\\"); 
      drive = DriveInfo.GetDrives() 
       .First(driveInfo => driveInfo.Name.Equals(drive, StringComparison.OrdinalIgnoreCase)).Name; 

      fixedPath = drive; 
     } 
     else 
     { 
      fixedPath = Directory.GetFileSystemEntries(fixedPath, token).First(); 
     } 
    } 

    return fixedPath; 
} 
4

j'aimais Yona's answer, mais je le voulais à:

  • support chemins UNC
  • Dites-moi si le chemin n'existait pas
  • Utilisez l'itération au lieu de la récursivité (car elle n'utilise que la récursion de queue)
  • Réduire le nombre d'appels à Path.Combine (pour réduire les concaténations de chaîne).
/// <summary> 
/// Gets the exact case used on the file system for an existing file or directory. 
/// </summary> 
/// <param name="path">A relative or absolute path.</param> 
/// <param name="exactPath">The full path using the correct case if the path exists. Otherwise, null.</param> 
/// <returns>True if the exact path was found. False otherwise.</returns> 
/// <remarks> 
/// This supports drive-lettered paths and UNC paths, but a UNC root 
/// will be returned in title case (e.g., \\Server\Share). 
/// </remarks> 
public static bool TryGetExactPath(string path, out string exactPath) 
{ 
    bool result = false; 
    exactPath = null; 

    // DirectoryInfo accepts either a file path or a directory path, and most of its properties work for either. 
    // However, its Exists property only works for a directory path. 
    DirectoryInfo directory = new DirectoryInfo(path); 
    if (File.Exists(path) || directory.Exists) 
    { 
     List<string> parts = new List<string>(); 

     DirectoryInfo parentDirectory = directory.Parent; 
     while (parentDirectory != null) 
     { 
      FileSystemInfo entry = parentDirectory.EnumerateFileSystemInfos(directory.Name).First(); 
      parts.Add(entry.Name); 

      directory = parentDirectory; 
      parentDirectory = directory.Parent; 
     } 

     // Handle the root part (i.e., drive letter or UNC \\server\share). 
     string root = directory.FullName; 
     if (root.Contains(':')) 
     { 
      root = root.ToUpper(); 
     } 
     else 
     { 
      string[] rootParts = root.Split('\\'); 
      root = string.Join("\\", rootParts.Select(part => CultureInfo.CurrentCulture.TextInfo.ToTitleCase(part))); 
     } 

     parts.Add(root); 
     parts.Reverse(); 
     exactPath = Path.Combine(parts.ToArray()); 
     result = true; 
    } 

    return result; 
} 

Pour les chemins UNC, ce cas, la racine (\\ Server \ Share) en cas de titre plutôt que le cas précis car il serait beaucoup plus de travail pour essayer de déterminer exactement du serveur distant nom de dossier et le nom de dossier exact du partage. Si vous souhaitez ajouter ce support, vous devrez utiliser des méthodes P/Invoke telles que NetServerEnum et NetShareEnum. Mais ceux-ci peuvent être lents et ne prennent pas en charge le filtrage initial uniquement sur le serveur et partagent les noms qui vous intéressent.

est ici une méthode de test unitaire pour TryGetExactPath (en utilisant Visual Studio Testing Extensions):

[TestMethod] 
public void TryGetExactPathNameTest() 
{ 
    string machineName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Environment.MachineName.ToLower()); 
    string[] testPaths = new[] 
     { 
      @"C:\Users\Public\desktop.ini", 
      @"C:\pagefile.sys", 
      @"C:\Windows\System32\cmd.exe", 
      @"C:\Users\Default\NTUSER.DAT", 
      @"C:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies", 
      @"C:\Program Files (x86)", 
      @"Does not exist", 
      @"\\Nas\Main\Setups", 
      @"\\Nas\Main\Setups\Microsoft\Visual Studio\VS 2015\vssdk_full.exe", 
      @"\\" + machineName + @"\C$\Windows\System32\ActionCenter.dll", 
      @"..", 
     }; 
    Dictionary<string, string> expectedExactPaths = new Dictionary<string, string>() 
     { 
      { @"..", Path.GetDirectoryName(Environment.CurrentDirectory) }, 
     }; 

    foreach (string testPath in testPaths) 
    { 
     string lowercasePath = testPath.ToLower(); 
     bool expected = File.Exists(lowercasePath) || Directory.Exists(lowercasePath); 
     string exactPath; 
     bool actual = FileUtility.TryGetExactPath(lowercasePath, out exactPath); 
     actual.ShouldEqual(expected); 
     if (actual) 
     { 
      string expectedExactPath; 
      if (expectedExactPaths.TryGetValue(testPath, out expectedExactPath)) 
      { 
       exactPath.ShouldEqual(expectedExactPath); 
      } 
      else 
      { 
       exactPath.ShouldEqual(testPath); 
      } 
     } 
     else 
     { 
      exactPath.ShouldBeNull(); 
     } 
    } 
} 
Questions connexes