在C#中规范化目录名称
这是问题,我有一堆像
S:\ HELLO \ HI
S:\ HELLO2 \ HI \ HElloAgain
在文件系统上,它将这些目录显示为
S:\你好\喜
S:\ hello2 \你好\ helloAgain
C#中是否有任何函数可以为我提供目录的文件系统名称与正确的大小写?
string FileSystemCasing = new System.IO.DirectoryInfo("H:\...").FullName;
编辑:
正如iceman指出的那样,只有当DirectoryInfo(或通常是FileSystemInfo)来自对GetDirectories(或GetFileSystemInfos)方法的调用时,FullName才会返回正确的大小写。
现在我发布经过测试和性能优化的解决方案。 它在目录和文件路径上都能很好地工作,并且在输入字符串上有一些容错能力。 它针对单个路径(不是整个文件系统)的“转换”进行了优化,并且比获取整个文件系统树更快。 当然,如果你必须重新规范整个文件系统树,你可能更喜欢冰人的解决方案,但我在中等深度的路径上测试10000次迭代,并且只需几秒钟;)
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"); }
这是一个基本且相对快速的解决方案,请继续阅读下面的一些评论:
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; } }
基本的想法是从DirectoryInfo对象获取子目录将获得正确的大小写,因此我们只需要拆分目录名称并从根目录步行到目标目录,在每一步获得正确的大小写。
我最初的答案依赖于为驱动器上的每个文件夹获取shell,但它工作但速度很慢。 我想出了一些存储结果的细微改进,但它对于日常使用来说仍然太慢了。 如果您需要为驱动器上的每件事物执行此操作,您可以看到此注释的编辑历史记录,即使这样,也可能有加速该代码的方法。 这是“你可能会这样做”而不是“这是一个很好的方式来做到这一点。”
Bertu在他的回答中提出了将路径分成其组件并逐件获得shell的想法,这导致速度大幅增加,因为您不再像我原来的答案那样检查所有内容 。 Bertu还推广了他的解决方案来做文件和目录。 在我的测试中,上面发布的代码(使用Bertu的“分割路径并按部分分离”的想法,但迭代地而不是递归地接近它)在Bertu代码的大约一半时间内运行。 我不确定是不是因为他的方法也处理文件,因为他使用递归会带来额外的开销,或者因为他最终在每次迭代中调用Path.GetFileName(path)
和Path.GetDirectoryName(path)
。 根据您的确切需求,他的答案和我的答案的某些组合可能会解决您的问题以及C#中的问题。
在这方面,我应该提到.Net文件名处理存在一些限制 ,因为在.Net中执行此操作需要制作大量的DirectoryInfo对象,如果这是您的瓶颈,您可能需要考虑非托管代码。
static void Main(string [] args) { string [] paths = new string [] {“S:\ hello \ Hi”,“S:\ hello2 \ Hi \ helloAgain”}; foreach(路径中的字符串aPath) { string normalizedPath = NormalizePath(aPath); Console.WriteLine(“Previous:'{0}',Normalized:'{1}'”,aPath,normalizedPath); } Console.Write(“\ n \ n \ n按任意键......”); Console.Read(); }
public static string NormalizePath( string path ) { StringBuilder sb = new StringBuilder( path ); string[] paths = path.Split('\\'); foreach( string folderName in paths ) { string normalizedFolderName = ToProperCase( folderName ); sb.Replace( folderName, normalizedFolderName ); } return sb.ToString(); } /// /// Converts a string to first character upper and rest lower (Camel Case). /// /// /// public static string ToProperCase( string stringValue ) { if( string.IsNullOrEmpty( stringValue ) ) return stringValue; return CultureInfo.CurrentCulture.TextInfo.ToTitleCase( stringValue.ToLower() ); }