使用.NET获取Windows上的实际文件名(具有适当的shell)

我想和这个问题完全一样:

Windows文件系统不区分大小写。 如何,给定一个文件/文件夹名称(例如“somefile”),我得到该文件/文件夹的实际名称(例如,如果资源pipe理器显示它应该返回“SomeFile”)?

但我需要在.NET中完成,我想要完整的path( D:/Temp/Foobar.xml而不是Foobar.xml )。

我看到FileInfo类的FullName没有办法。

我似乎是因为NTFS是不区分大小写的,它总是会正确接受你的输入,不管名字是否正确。

得到正确路径名的唯一方法似乎是找到像John Sibly建议的那样的文件。

我创建了一个方法,将采取一个路径(文件夹或文件),并返回它的正确套用版本(整个路径):

  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(); } } 

以下是在我的机器上运行的一些测试用例:

  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(); } 

如果文件不存在,该方法将返回提供的值。

有可能是更快的方法(这使用递归),但我不知道是否有任何明显的方法来做到这一点。

受到Ivan的回答的启发,这里也是一个处理驱动器盘符的方法:

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

我喜欢Yona的回答 ,但我希望它:

  • 支持UNC路径
  • 告诉我,如果路径不存在
  • 使用迭代而不是递归(因为它只使用尾递归)
  • 最大限度地减少对Path.Combine的调用次数(以最小化字符串连接)。
 /// <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 (eg, \\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 (ie, 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; } 

对于UNC路径,这种情况下,标题情况下的根(\\ server \ Share)而不是确切的大小写,因为尝试确定远程服务器的确切大小写名称和共享的确切大小写名称将有很多工作要做。 如果你有兴趣添加支持,你将不得不像NetserverEnum和NetShareEnum这样的P / Invoke方法。 但是这些可能会很慢,并且不支持对服务器和共享名称进行预先筛选。

下面是TryGetExactPath的单元测试方法(使用Visual Studio测试扩展 ):

 [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(); } } } 

我的第二个答案在这里用非递归方法。 它接受文件和目录。
这段时间从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; } 

我认为唯一能够做到这一点的方法是使用相同的Win32 API,即在您接受的问题的接受答案中提到的SHGetFileInfo方法。 为了做到这一点,你需要使用一些互操作性的p / invoke调用。 看一下pinvoke.net的例子,了解如何做到这一点,以及您将需要哪些额外的结构。

有趣的问题。

一种方法是基于不区分大小写的名称“查找”文件,然后查看FileInfo.FullName属性。 我已经测试了这个使用下面的函数,它给出了所需的结果。

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

在这里你需要小心一点 – 如果你有两个文件叫做file.xml和File.xml,那么它只会返回第一个文件。

看起来最好的方法是遍历路径中的所有文件夹,并获得适当的上限:

  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 

你有没有尝试DirectoryInfo和路径类,他们可能会做的伎俩。 (自己没有尝试过)