Normalize directory names in C#
string FileSystemCasing = new System.IO.DirectoryInfo("H:\...").FullName;
EDIT:
As iceman pointed out, the FullName returns the correct casing only if the DirectoryInfo (or in general the FileSystemInfo) comes from a call to the GetDirectories (or GetFileSystemInfos) method.
Now I'm posting a tested and performance-optimized solution. It work well both on directory and file paths, and has some fault tolerance on input string. It's optimized for "conversion" of single paths (not the entire file system), and faster than getting the entire file system tree. Of course, if you have to renormalize the entire file system tree, you may prefer iceman's solution, but i tested on 10000 iterations on paths with medium level of deepness, and it takes just few seconds ;)
private string GetFileSystemCasing(string path)
{
if (Path.IsPathRooted(path))
{
path = path.TrimEnd(Path.DirectorySeparatorChar); // if you type c:\foo\ instead of c:\foo
try
{
string name = Path.GetFileName(path);
if (name == "") return path.ToUpper() + Path.DirectorySeparatorChar; // root reached
string parent = Path.GetDirectoryName(path); // retrieving parent of element to be corrected
parent = GetFileSystemCasing(parent); //to get correct casing on the entire string, and not only on the last element
DirectoryInfo diParent = new DirectoryInfo(parent);
FileSystemInfo[] fsiChildren = diParent.GetFileSystemInfos(name);
FileSystemInfo fsiChild = fsiChildren.First();
return fsiChild.FullName; // coming from GetFileSystemImfos() this has the correct case
}
catch (Exception ex) { Trace.TraceError(ex.Message); throw new ArgumentException("Invalid path"); }
return "";
}
else throw new ArgumentException("Absolute path needed, not relative");
}
Here's a basic and relatively fast solution, keep reading below for some commentary:
private static string GetCase(string path)
{
DirectoryInfo dir = new DirectoryInfo(path);
if (dir.Exists)
{
string[] folders = dir.FullName.Split(Path.DirectorySeparatorChar);
dir = dir.Root;
foreach (var f in folders.Skip(1))
{
dir = dir.GetDirectories(f).First();
}
return dir.FullName;
}
else
{
return path;
}
}
The basic idea is that getting subdirectories from a DirectoryInfo object will get you the correct case, so we just need to split the directory name and walk from the root to the target directory, getting the proper case at each step.
My initial answer relied on getting the casing for every folder on the drive, and it worked but was slow. I came up with a slight improvement that stored the results, but it was still too slow for everyday usage. You can see the edit history for this comment if you need to do this for every thing on the drive, and even then there are probably ways to speed up that code. It was "here's how you might do it" and not "here's a great way to do it."
Bertu, in his answer, came up with the idea of splitting the path into its components and getting the casing piece by piece, which results in a huge speed increase since you're no longer checking everything as in my original answer. Bertu also generalized his solution to do files as well as directories. In my tests, the code posted above (which uses Bertu's "split the path and do it by pieces" idea but approaches it iteratively instead of recursively) runs in about half the time of Bertu's code. I'm not sure if that's because his method also handles files, because his use of recursion introduces extra overhead, or because he ends up calling Path.GetFileName(path)
and Path.GetDirectoryName(path)
in each iteration. Depending on your exact needs, some combination of his answer and mine will likely solve your problem as well as is possible in C#.
On that note, I should mention that there are some limitations to .Net filename handling, and since doing this in .Net requires making a lot of DirectoryInfo objects, you might want to consider unmanaged code if this is your bottleneck.