Подтвердить что ты не робот

Нормализовать имена каталогов в С#

Здесь проблема, у меня есть куча каталогов, таких как

S:\ПРИВЕТ\HI
S:\hello2\HI\HElloAgain

В файловой системе эти каталоги отображаются как

S:\привет\Привет
S:\hello2\Hi\helloAgain

Есть ли какая-либо функция в С#, которая даст мне то, что имя файловой системы каталога с соответствующим корпусом?

4b9b3361

Ответ 1

string FileSystemCasing = new System.IO.DirectoryInfo("H:\...").FullName;

EDIT:

Как отметил iceman, FullName возвращает правильную оболочку только в том случае, если DirectoryInfo (или вообще FileSystemInfo) происходит от вызова метода GetDirectories (или GetFileSystemInfos).

Теперь я отправляю протестированное и оптимизированное по производительности решение. Он хорошо работает как по каталогам, так и по файловым путям и имеет некоторую отказоустойчивость на входной строке. Он оптимизирован для "преобразования" одиночных путей (не всей файловой системы) и быстрее, чем получение всего дерева файловой системы. Конечно, если вам нужно перенормировать все дерево файловой системы, вы можете предпочесть решение iceman, но я тестировал 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");
    }

Ответ 2

Здесь основное и относительно быстрое решение, читайте ниже для комментариев:

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. Я не уверен, что это потому, что его метод также обрабатывает файлы, потому что его использование рекурсии приводит к дополнительным накладным расходам или потому, что он заканчивает вызов Path.GetFileName(path) и Path.GetDirectoryName(path) на каждой итерации. В зависимости от ваших конкретных потребностей какая-то комбинация его ответа и моего, скорее всего, решит вашу проблему, а также возможно на С#.

В этой заметке я должен упомянуть, что есть некоторые ограничения для обработки .Net файлов, и поскольку для этого в .Net требуется много объектов DirectoryInfo, вы можете рассмотреть неуправляемый код, если это ваше узкое место.

Ответ 3

Это должно сделать это:

System.IO.Directory.GetFiles("...");

Ответ 4

    static void Main (string [] args)     {        string [] paths = new string [] { "S:\hello\Hi", "S:\hello2\Hi\helloAgain" };        foreach (строка aPath в путях)        {           строка 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();
  }

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