我想和this question完全一样:
Windows文件系统不区分大小写。如果给定一个文件/文件夹名称(例如“somefile”),我得到该文件/文件夹的实际名称(例如,如果Explorer显示它,它应该返回“SomeFile”)?
但我需要在.NET中完成它,我想要完整路径(D:/Temp/Foobar.xml
而不仅仅是Foobar.xml
)。
我发现FullName
上的FileInfo
不能解决问题。
答案 0 :(得分:24)
我似乎因为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();
}
如果文件不存在,该方法将返回提供的值。
可能有更快的方法(这使用递归),但我不确定是否有任何明显的方法可以做到。
答案 1 :(得分:8)
我喜欢Yona's answer,但我希望它:
/// <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;
}
对于UNC路径,这会出现标题情况下的根(\\ Server \ Share),而不是确切的情况,因为尝试确定远程服务器时,很多工作量会更多确切的案例名称和共享的确切案例名称。如果您有兴趣添加该支持,则必须使用P / Invoke方法,例如NetServerEnum和NetShareEnum。但这些可能很慢,而且他们不支持对服务器进行预先过滤并共享您关注的名称。
这是TryGetExactPath的单元测试方法(使用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();
}
}
}
答案 2 :(得分:5)
受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;
}
答案 3 :(得分:4)
我的第二个答案是非递归方法。它接受文件和目录 这次从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;
}
答案 4 :(得分:1)
我认为你能够做到这一点的唯一方法是使用相同的Win32 API,即SHGetFileInfo方法,在你引用的问题的接受答案中提到。为此,您需要使用一些互操作p / invoke调用。请查看pinvoke.net,了解如何执行此操作以及您需要的其他结构。
答案 5 :(得分:0)
有趣的问题。
一种方法是根据不区分大小写的名称“查找”文件,然后查看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,那么它只会返回第一个。
答案 6 :(得分:0)
看起来最好的方法是遍历路径中的所有文件夹并获得正确的上限:
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