簡體   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