在C#中规范化目录名称

时间:2009-07-31 19:47:32

标签: c# .net

这是问题,我有一堆像

这样的目录
  

S:\ HELLO \ HI
  S:\ HELLO2 \ HI \ HElloAgain

在文件系统上,它将这些目录显示为

  

S:\你好\您好
  S:\ hello2 \您好\ helloAgain

C#中是否有任何函数可以为我提供目录的文件系统名称和正确的大小写?

4 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:4)

这是一个基本且相对快速的解决方案,请继续阅读下面的一些评论:

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对象获取子目录将获得正确的大小写,因此我们只需要拆分目录名称并从根目录步行到目标目录,在每一步都获得正确的大小写。

我最初的答案依赖于为驱动器上的每个文件夹获取外壳,但它工作但速度很慢。我想出了一些存储结果的细微改进,但它对于日常使用来说仍然太慢了。如果您需要为驱动器上的每件事物执行此操作,您可以看到此注释的编辑历史记录,即使这样,也可能有加速该代码的方法。这是“你可以做到这一点”,而不是“这是一个很好的方法。”

Bertu在他的回答中提出了将路径分成其组件并逐个获得套管的想法,这导致巨大速度增加,因为您不再检查一切与我原来的答案一样。 Bertu还推广了他的解决方案来做文件和目录。在我的测试中,上面发布的代码(使用Bertu的“分割路径并按部分分离”的想法,但迭代地而不是递归地接近它)在Bertu代码的大约一半时间内运行。我不确定是不是因为他的方法也处理文件,因为他使用递归会带来额外的开销,或者因为他最终在每次迭代中调用Path.GetFileName(path)Path.GetDirectoryName(path)。根据您的确切需求,他的答案和我的答案的某些组合可能会解决您的问题以及C#中的问题。

就此而言,我应该提到{。}}到.Net文件名处理,因为在.Net中这样做需要制作大量的DirectoryInfo对象,如果这是你的,你可能想要考虑非托管代码瓶颈。

答案 2 :(得分:0)

我的版本(类似于使用GetDirectories()GetFileSystemInfos()的{​​{3}}和BertuPG的版本,但是允许不存在的路径和文件并用作扩展方法):

/// <summary>
/// Gets the full path with all the capitalization properly done as it exists in the file system.
/// Non-existent paths returned as passed in.
/// </summary>
/// <param name="path">
/// The path to make full and make properly cased.
/// </param>
/// <returns>
/// The complete and capitalization-accurate path based on the 
/// given <paramref name="path"/>.
/// </returns>
/// <remarks>
/// Walks the path using <see cref="DirectoryInfo"/>.<see cref="DirectoryInfo.GetDirectories()"/>
/// and <see cref="DirectoryInfo.GetFileSystemInfos()"/> so don't expect awesome performance.
/// </remarks>
public static string GetFileSystemFullPath(this string path)
{
    if (path == null)
    {
        return path;
    }

    string[] pathParts = Path.GetFullPath(path).Split(Path.DirectorySeparatorChar);
    if (pathParts.Any())
    {
        var dir = new DirectoryInfo(pathParts[0]).Root;

        for (int i = 1; i < pathParts.Length; ++i)
        {
            var next = dir.GetDirectories(pathParts[i]).FirstOrDefault();
            if (next == null)
            {
                if (i == pathParts.Length - 1)
                {
                    var fileInfo = dir.GetFileSystemInfos(pathParts[i]).FirstOrDefault();
                    if (fileInfo != null)
                    {
                        return fileInfo.FullName;
                    }
                }

                var ret = dir.FullName;
                do
                {
                    ret = Path.Combine(ret, pathParts[i++]);
                } while (i < pathParts.Length);

                return ret;
            }

            dir = next;
        }

        return dir.FullName;
    }

    // cannot do anything with it.
    return path;
}

答案 3 :(得分:-3)

      static void Main( string[] args )
      {
         string[] paths = new string[] { "S:\hello\Hi", "S:\hello2\Hi\helloAgain" };
         foreach( string aPath in paths )
         {
            string normalizedPath = NormalizePath( aPath );
            Console.WriteLine( "Previous: '{0}', Normalized: '{1}'", aPath, normalizedPath );
         }
         Console.Write( "\n\n\nPress any key..." );
         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();
  }

  /// <summary>
  /// Converts a string to first character upper and rest lower (Camel Case).
  /// </summary>
  /// <param name="stringValue"></param>
  /// <returns></returns>
  public static string ToProperCase( string stringValue )
  {
     if( string.IsNullOrEmpty( stringValue ) )
        return stringValue;

     return CultureInfo.CurrentCulture.TextInfo.ToTitleCase( stringValue.ToLower() );
  }