繁体   English   中英

在C#中规范化目录名称

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

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

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

在这方面,我应该提到.Net文件名处理有一些限制 ,因为在.Net中这样做需要制作大量的DirectoryInfo对象,如果这是你的瓶颈,你可能想要考虑非托管代码。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM