
Как посчитать количество файлов в папке на VB.NET
Посчитать файлы в папке на VB.NET можно одной строкой. Но на большом диске эта строка либо съест память, либо упадёт на первой закрытой папке. Разбираем, как считать надёжно.
Одна строка для простого случая
В VB6 для подсчёта файлов приходилось крутить цикл с функцией Dir или обходить коллекцию Files объекта FileSystemObject. В VB.NET класс System.IO.Directory возвращает массив имён файлов, и количество равно длине этого массива.
Для папки с сотней файлов этого вполне достаточно. Функцию удобно положить в модуль и вызывать из любой формы, например MsgBox(GetFileCount(«C:\Temp»)). Метод GetFiles считает только файлы, вложенные папки в результат не попадают.
Imports System.IO
Public Module FileCount
' Количество файлов в папке без вложенных
Public Function GetFileCount(path As String) As Integer
Return Directory.GetFiles(path).Length
End Function
' Количество файлов по маске, например "*.jpg"
Public Function GetFileCount(path As String, mask As String) As Integer
Return Directory.GetFiles(path, mask).Length
End Function
End ModuleПодсчёт с вложенными папками
Чтобы посчитать файлы во всём дереве, рекурсию писать не нужно: у GetFiles есть третий параметр SearchOption.AllDirectories. Вызов Directory.GetFiles(path, «*.*», SearchOption.AllDirectories).Length вернёт общее количество файлов во всех подпапках.
У этого варианта две серьёзные проблемы. Первая: метод сначала строит полный массив строк и только потом отдаёт его. Для диска с миллионом файлов это сотни мегабайт памяти и долгая пауза, во время которой интерфейс висит. Вторая: если хотя бы одна подпапка закрыта правами, весь вызов падает с UnauthorizedAccessException, и вы не получаете вообще ничего.
EnumerateFiles: считаем на лету
В .NET Framework 4 появился метод Directory.EnumerateFiles. Он возвращает последовательность, которая читает файловую систему порциями по мере перебора. Массив целиком не строится, первые результаты приходят сразу, а память почти не расходуется.
Для подсчёта достаточно пройти последовательность и увеличить счётчик. Метод Count из LINQ делает ровно это. Обратите внимание на тип Long: на файловом сервере количество файлов легко выходит за пределы Integer.
Imports System.IO
Imports System.Linq
Public Module FileCountLazy
' .NET 4+: обход без построения массива
Public Function CountAll(path As String) As Long
Return Directory.EnumerateFiles(path, "*",
SearchOption.AllDirectories).LongCount()
End Function
End ModuleОбход закрытых папок
EnumerateFiles решает проблему памяти, но не проблему прав: встретив папку System Volume Information или чужой профиль, перечисление тоже бросит исключение и остановится. Если нужно посчитать всё, до чего программа может дотянуться, придётся обходить дерево самостоятельно и ловить ошибки по каждой папке отдельно.
Вместо рекурсии удобно использовать стек: так глубокая структура каталогов не приведёт к переполнению стека вызовов, а код остаётся коротким. Папки, которые не удалось прочитать, можно собрать в список и показать пользователю.
Imports System.IO
Imports System.Collections.Generic
Public Module SafeFileCount
' Считает файлы во всём дереве, пропуская недоступные папки
Public Function CountFilesSafe(root As String, skipped As List(Of String)) As Long
Dim total As Long = 0
Dim pending As New Stack(Of String)
pending.Push(root)
While pending.Count > 0
Dim dir = pending.Pop()
Try
total += Directory.EnumerateFiles(dir).LongCount()
For Each subDir In Directory.EnumerateDirectories(dir)
pending.Push(subDir)
Next
Catch ex As UnauthorizedAccessException
skipped.Add(dir) ' нет прав, идём дальше
Catch ex As PathTooLongException
skipped.Add(dir) ' путь длиннее 260 символов
End Try
End While
Return total
End Function
End ModuleМаски и скрытые файлы
Маска в GetFiles и EnumerateFiles работает почти как в командной строке, но с одной исторической странностью. Если в маске расширение из трёх символов, например «*.htm», будут найдены и файлы .html, потому что Windows сравнивает ещё и короткие имена 8.3. Если важна точность, фильтруйте результат дополнительно по Path.GetExtension.
Скрытые и системные файлы методы возвращают наравне с обычными. Проводник по умолчанию их не показывает, поэтому числа могут не совпасть с тем, что видит пользователь. Чтобы считать как Проводник, проверяйте атрибуты через File.GetAttributes или используйте DirectoryInfo.EnumerateFiles и свойство Attributes у FileInfo.
- thumbs.db и desktop.ini: типичные скрытые файлы, которые раздувают счётчик;
- FileAttributes.Hidden и FileAttributes.System проверяются побитовым And;
- FileInfo уже содержит атрибуты, лишнего обращения к диску не будет.
Не блокируйте интерфейс
Подсчёт большого дерева на сетевом диске может занимать минуты. Если вызвать его в обработчике кнопки, форма перестанет отвечать, и Windows пометит программу как зависшую. Выносите обход в BackgroundWorker или отдельный поток и сообщайте прогресс, например текущую папку и промежуточный счётчик.
Как это делают сегодня
Вопросы и ответы
Почему GetFiles с маской *.htm находит файлы .html?
Windows сравнивает маску и с длинным, и с коротким именем файла 8.3, а короткое расширение у .html выглядит как .HTM. Фильтруйте результат по Path.GetExtension, если нужна точность.
Как посчитать файлы и папки вместе?
Используйте Directory.EnumerateFileSystemEntries: метод возвращает и файлы, и подкаталоги. Для раздельного подсчёта вызовите EnumerateFiles и EnumerateDirectories.
Сколько памяти занимает GetFiles на большом диске?
Каждая строка пути занимает около сотни байт и больше, поэтому миллион файлов даёт сотни мегабайт. EnumerateFiles читает каталог порциями и почти не расходует память.