Существует ли более быстрый способ просканировать через каталог рекурсивно в.NET?

Ваш класс Blog содержит только поля, а не свойства, поэтому DefaultModelBinder не может установить свои значения. Изменить это добавить getters / setters

public class Blog
{
    public string Name { get; set; }
    public string URL { get; set; }
}
27
задан Sam Saffron 7 April 2009 в 04:33
поделиться

5 ответов

Эта реализация, для которой нужно немного тонкой настройки, 5-10X быстрее.

    static List<Info> RecursiveScan2(string directory) {
        IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
        WIN32_FIND_DATAW findData;
        IntPtr findHandle = INVALID_HANDLE_VALUE;

        var info = new List<Info>();
        try {
            findHandle = FindFirstFileW(directory + @"\*", out findData);
            if (findHandle != INVALID_HANDLE_VALUE) {

                do {
                    if (findData.cFileName == "." || findData.cFileName == "..") continue;

                    string fullpath = directory + (directory.EndsWith("\\") ? "" : "\\") + findData.cFileName;

                    bool isDir = false;

                    if ((findData.dwFileAttributes & FileAttributes.Directory) != 0) {
                        isDir = true;
                        info.AddRange(RecursiveScan2(fullpath));
                    }

                    info.Add(new Info()
                    {
                        CreatedDate = findData.ftCreationTime.ToDateTime(),
                        ModifiedDate = findData.ftLastWriteTime.ToDateTime(),
                        IsDirectory = isDir,
                        Path = fullpath
                    });
                }
                while (FindNextFile(findHandle, out findData));

            }
        } finally {
            if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle);
        }
        return info;
    }

дополнительный метод:

 public static class FILETIMEExtensions {
        public static DateTime ToDateTime(this System.Runtime.InteropServices.ComTypes.FILETIME filetime ) {
            long highBits = filetime.dwHighDateTime;
            highBits = highBits << 32;
            return DateTime.FromFileTimeUtc(highBits + (long)filetime.dwLowDateTime);
        }
    }

interop defs:

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr FindFirstFileW(string lpFileName, out WIN32_FIND_DATAW lpFindFileData);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATAW lpFindFileData);

    [DllImport("kernel32.dll")]
    public static extern bool FindClose(IntPtr hFindFile);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct WIN32_FIND_DATAW {
        public FileAttributes dwFileAttributes;
        internal System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
        internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
        internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
        public int nFileSizeHigh;
        public int nFileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
    }
39
ответ дан Sam Saffron 28 November 2019 в 03:03
поделиться

Это довольно мелкий, 371 каталог со средним числом 10 файлов в каждом каталоге. некоторые каталоги содержат другие суб-каталоги

Это просто комментарий, но ваши цифры действительно довольно высоки. Я запустил приведенное ниже, используя, по сути, тот же рекурсивный метод, который вы используете, и мое время намного меньше, несмотря на создание вывода строки.

    public void RecurseTest(DirectoryInfo dirInfo, 
                            StringBuilder sb, 
                            int depth)
    {
        _dirCounter++;
        if (depth > _maxDepth)
            _maxDepth = depth;

        var array = dirInfo.GetFileSystemInfos();
        foreach (var item in array)
        {
            sb.Append(item.FullName);
            if (item is DirectoryInfo)
            {
                sb.Append(" (D)");
                sb.AppendLine();

                RecurseTest(item as DirectoryInfo, sb, depth+1);
            }
            else
            { _fileCounter++; }

            sb.AppendLine();
        }
    }

Я запустил приведенный выше код для ряда различных каталогов. На моей машине второй вызов для сканирования дерева каталогов обычно выполнялся быстрее из-за кэширования как во время выполнения, так и в файловой системе. Обратите внимание, что эта система не является чем-то особенным, это всего лишь 1-летняя рабочая станция для разработки.

// cached call
Dirs = 150, files = 420, max depth = 5
Time taken = 53 milliseconds

// cached call
Dirs = 1117, files = 9076, max depth = 11
Time taken = 433 milliseconds

// first call
Dirs = 1052, files = 5903, max depth = 12
Time taken = 11921 milliseconds

// first call
Dirs = 793, files = 10748, max depth = 10
Time taken = 5433 milliseconds (2nd run 363 milliseconds)

Обеспокоенный тем, что я не получил дату создания и изменения, код был изменен, чтобы вывести это также в следующие моменты времени.

// now grabbing last update and creation time.
Dirs = 150, files = 420, max depth = 5
Time taken = 103 milliseconds (2nd run 93 milliseconds)

Dirs = 1117, files = 9076, max depth = 11
Time taken = 992 milliseconds (2nd run 984 milliseconds)

Dirs = 793, files = 10748, max depth = 10
Time taken = 1382 milliseconds (2nd run 735 milliseconds)

Dirs = 1052, files = 5903, max depth = 12
Time taken = 936 milliseconds (2nd run 595 milliseconds)

Примечание. Класс System.Diagnostics.StopWatch используется для синхронизации.

2
ответ дан Robert Paulson 14 October 2019 в 13:33
поделиться

Я бы использовал или основывал себя на этой многопоточной библиотеке: http://www.codeproject.com/KB/files/FileFind.aspx

1
ответ дан Bertvan 14 October 2019 в 13:33
поделиться

В зависимости от того, сколько времени Вы пытаетесь сбрить функцию, это может стоить Вашего, в то время как назвать API-функции Win32 непосредственно, так как существующий API делает большую дополнительную обработку для проверки вещей, которыми Вы не можете интересоваться.

Если Вы поэтому уже не сделали, и предположение, что Вы не намереваетесь способствовать Моно проекту, я настоятельно рекомендовал бы загрузить Отражатель и взглянуть на то, как Microsoft реализовала вызовы API, которые Вы в настоящее время используете. Это даст Вам общее представление о том, что необходимо назвать и что можно не учесть.

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

5
ответ дан tylerl 28 November 2019 в 03:03
поделиться

попробуйте это (т.е. сделайте инициализацию сначала и затем снова используйте Ваш список и Ваши объекты directoryInfo):

  static List<Info> RecursiveMovieFolderScan1() {
      var info = new List<Info>();
      var dirInfo = new DirectoryInfo(path);
      RecursiveMovieFolderScan(dirInfo, info);
      return info;
  } 

  static List<Info> RecursiveMovieFolderScan(DirectoryInfo dirInfo, List<Info> info){

    foreach (var dir in dirInfo.GetDirectories()) {

        info.Add(new Info() {
            IsDirectory = true,
            CreatedDate = dir.CreationTimeUtc,
            ModifiedDate = dir.LastWriteTimeUtc,
            Path = dir.FullName
        });

        RecursiveMovieFolderScan(dir, info);
    }

    foreach (var file in dirInfo.GetFiles()) {
        info.Add(new Info()
        {
            IsDirectory = false,
            CreatedDate = file.CreationTimeUtc,
            ModifiedDate = file.LastWriteTimeUtc,
            Path = file.FullName
        });
    }

    return info; 
}
0
ответ дан Jimmy 28 November 2019 в 03:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: