Что нового

_FileDirList - Поиск файлов с использованием команды DIR

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
в "окошках" есть такой момент, что, если ищешь повторно, то времени нужно уже меньше. то есть, если подряд запустить две разных функции на поиск, то вторая сработает быстрее.. поэтому тесты проводить нужно в разных вариантах.
erlik
думаю, что ты так и делал.
теперь по скорости конкретных функций
_RecFileListToArray - 146843 файлов за 7.21 сек
_FO_FileSearch - 146843 файлов 3.66 сек
_FileDirList - 146162 файлов 6.89 сек
для поиска брался один и тот же диск
_FileDirList - возвращает разное количество файлов
145963-6.54 сек
146654-7.12 сек и так далее , что позволяет не использовать такой метод

самая быстрая функция - _FO_FileSearch
 

erlik

Продвинутый
Сообщения
317
Репутация
84
joiner
думаю, что ты так и делал
Да ничего особенного :smile:
Код:
$aData = _RecFileListToArray($sDir, $sFileMask, $iFlag, $iSubDir, 0, 2)
и
$aData = _FileDirList($sDir, $sFileMask, $iFlag, $iSubDir)

В качестве $sDir использовались разделы по 40 и 75 гигов. В качестве флагов поиска: - поиск папок и файлов, поиск в подпапках.
_RecFileListToArray у меня всегда давала лучший результат, поэтому ее и использовал. То что _FileDirList за счет особенностей dir вторично дает более быстрый результат я в курсе, но в моем случае не аргумент: первичный поиск приоритетнее. И она, как ты сам заметил, возвращает разное количество файлов.
_FO_FileSearch особо не пользовался. Но сейчас вставил ее в свою прогу - результат на уровне _RecFileListToArray. Но повторный поиск дает многократный (раз в 6) прирост скорости - как и у _FileDirList. Видимо как то результаты поиска сохраняются ( в реализацию функции я не углублялся). Так что что выбирать - однозначно сказать сложно.
К тому же _FO_FileSearch возвращала у меня меньшее число файлов и папок, нежели _RecFileListToArray.
ЗЫ: Ну еще не стоит забывать, что разные результаты могут быть следствием разной файловой структуры дисков, которые мы тестили.
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
erlik [?]
К тому же _FO_FileSearch возвращала у меня меньшее число файлов и папок, нежели _RecFileListToArray.
у меня ни разу разницы не было. диск занят на 190Гб в нтфс
насчет скорости, в любых вариантах _FO_FileSearch выигрывает по скорости
да, забыл, _FO_FileSearch это только для поиска файлов
 

AZJIO

Меценат
Меценат
Сообщения
2,892
Репутация
1,196
erlik
_FO_FileSearch и _RecFileListToArray должны выдавать одинаковое количество файлов, так как принципиальный способ детектирования файлов практически одинаков - формирование регулярного выражения. Разница в скорости только в том что _RecFileListToArray проверяет маску для каждого файла в цикле, а _FO_FileSearch сначала формирует список, а потом за один проход регулярным выражением удаляет несоответствующие маске. Вот этот "один проход" и даёт выигрыш по времени. _RecFileListToArray может выдать меньше потому что там могут отключены скрытые файлы. А вообще чтобы определить разницу достаточно получить два списка и сравнить их в программе сравнения строк. Это перво-наперво делается чтобы определить ошибки.

Семёнычев Роман сказал(а):
вообще в справке к FileFindFirstFile сказано
Можно использовать только одну маску в имени файла и в его расширении, например a*.b?.
Это относится не конкретно к указанной функции, а к движку с подстановочными символами (wildcards). Фактически у меня "conflicted copy*" выдаёт тоже что и "*conflicted copy*". Поэтому видимо ошибки которые я указывал в предыдущем посте этой темы было связано с этим ограничением.
 

erlik

Продвинутый
Сообщения
317
Репутация
84
AZJIO
Твоя функция однозначно в лидерах. joiner был прав - я не так тестировал.
Нужно после каждого поиска перезагружать систему (кстати joiner - твои результаты раз в 10 лучше моих, что очень странно), иначе результат следующего поиска любой функцией будет неверным (более быстрым).
Тестил теперь только на поиске файлов, так как _FO_FileSearch ищет только их, потому и разница в кол-ве была.
Итог был для меня неожиданным:
_FileDirList оказалась быстрее _RecFileListToArray. В своем первом посте я ориентировался на результаты давнишней проверки на XP, но то ли я тогда неверно тестил, то ли на семерке dir лучше работает, но получилось все наоборот.
Но _FO_FileSearch обошла обе предыдущие функции. Так что для поиска только файлов - лидер однозначен. У файл _FileDirList существенный недостаток в том, что она выдает разное число найденных файлов при каждом проходе, поэтому для поиска файлов и папок все таки _RecFileListToArray надежнее.
 

AZJIO

Меценат
Меценат
Сообщения
2,892
Репутация
1,196
erlik [?]
Нужно после каждого поиска перезагружать систему
Как раз таки наоборот. При перезагрузке вы тестите не скорость функции, а скорость доступа жёсткого диска, то есть текущего состояния его кеш-памяти. Надо тестить одну и туже папку при чём первый поиск считать нетестовым а формирующим кеш, остальные запросы той же папки уже будут тестировать скорость алгоритма.
 

erlik

Продвинутый
Сообщения
317
Репутация
84
AZJIO
Окей. Понял. Значит тогда мои результаты близки к тем, что у joiner'а.
А скорость доступа к диску у меня действительно весьма медленная.
Код:
 _FileDirList          91483  файлов   -  36464 мск,    повторно:  91486  файлов  - 5749 мск, третий проход: 91341 файлов  - 5836 мск
_RecFileListToArray   91486  файлов   - 153511 мск,    повторно:  91486  файлов  - 7737 мск, третий проход: 91486 файлов  - 7749 мск
_FO_FileSearch        91486  файлов   -  33096 мск,    повторно:  91486  файлов  - 4231 мск, третий проход: 91486 файлов  - 4226 мск
 

AZJIO

Меценат
Меценат
Сообщения
2,892
Репутация
1,196
Семёнычев Роман [?]
однако работает ) даже с двумя масками все работает и находит
Если оно иногда работает, а иногда что-то находит - это не совсем то, что требуется любому здравомыслящему программисту. Если вас устраивает, я не против, ваше право. Если в справке указано правило, то при отклонении от этого правила Microsoft вам не гарантирует требуемого результата.

[?]
даже с двумя масками
Что имеется ввиду под двумя масками? Двумя подстановочными знаками или перечисление нескольких масок поиска? Если имеется подстановочные знаки, то в теории *слово* и слово* не являются одинаковыми масками и должны находить разные результаты. Первый вариант позволяет начинаться с любого другого символа, слово может быть в конце. Второй вариант обязывает начинаться с заданного слова. Это разные маски. Если речь идёт о нескольких поисковых масок, то их поддержка объявлена в описании функции (см. пример в первом посте).
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
еще раз делал сравнения функций поиска. причина - не поиск самой быстрой, а самой точной.
не буду приводить примеры других. пишу в этой теме, так как это касается функции _FileDirList
Выше уже обсуждалось, что функция возвращает разное количество файлов, что делает функцию _FileDirList совершенно непригодной к применению
я делал поиск непосредственно в ком.строке системы и все разы был правильный возврат количества файлов.
пришел к выводу, что в функции чтение из консоли неверно. точнее , при чтении не получаются те результаты, которые выдает консоль системы
я немного изменил функцию. перенаправив результаты поиска в буфер обмена. это увеличило время работы функции, но добился получения верного результата в любом случае
Код:
#include <Array.au3>

;Используется буфер обмена===========================================
$a = TimerInit()
Local $AllFiles = _FileDirList_('C:\Program Files (x86)','*',1); получаем строку для консоли - DIR "C:\Program Files (x86)\*" /B /S /A-D
Local $CorrectString = StringTrimRight($AllFiles,1)
$Result = StringSplit($CorrectString, @LF)
ConsoleWrite('_FileDirList+Буфер обмена: Время поиска - ' & TimerDiff($a) & '; Количество файлов - ' & $Result[0] & @LF)
_ArrayDisplay($Result,'Используем буфер обмена')
Func _FileDirList_($sPath, $sFileMask = "*", $iFlag = 0, $iSubDir = 1, $iSort = 0)
	Local $sOutBin, $sOut, $aOut, $aMasks, $sRead, $hDir, $sAttrib, $sFiles
	If Not StringInStr(FileGetAttrib($sPath), "D") Then
		Return SetError(1, 0, 0)
	EndIf
	If $iSubDir = 1 Then
		$sAttrib &= ' /S'
	EndIf

	If $iSort = 1 Then
		$sAttrib &= ' /O:N'
	EndIf
	Switch $iFlag
		Case 1
			$sAttrib &= ' /A-D'
		Case 2
			$sAttrib &= ' /AD'
		Case Else
			$sAttrib &= ' /A'
	EndSwitch
	$sOut = StringToBinary('0' & @CRLF, 2)
	$sPath = StringRegExpReplace($sPath, '\\+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, '^;+|;+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, ';{2,}', ';')
	$aMasks = StringSplit($sFileMask, ';')
	For $i = 1 To $aMasks[0]
		If StringStripWS($aMasks[$i], 8) = "" Then
			ContinueLoop
		EndIf

		$sFiles &= '"' & $sPath & '\' & $aMasks[$i] & '"'

		If $i < $aMasks[0] Then
			$sFiles &= ';'
		EndIf
	Next
	$hDir = RunWait(@ComSpec & ' /U /C DIR ' & $sFiles & ' /B' & $sAttrib & ' | CLIP', @SystemDir, @SW_HIDE, 6)
	Return ClipGet()
EndFunc
;==================================================================================================================

;Оригинальный вариант функции======================================================================================
$a = TimerInit()
Local $AllFiles = _FileDirList('C:\Program Files (x86)','*',1); получаем строку для консоли - DIR "C:\Program Files (x86)\*" /B /S /A-D
ConsoleWrite('_FileDirList: Время поиска - ' & TimerDiff($a) & '; Количество файлов - ' & $AllFiles[0] & @LF)
_ArrayDisplay($AllFiles,'Чтение из консоли')
Func _FileDirList($sPath, $sFileMask = "*", $iFlag = 0, $iSubDir = 1, $iSort = 0)
	Local $sOutBin, $sOut, $aOut, $aMasks, $sRead, $hDir, $sAttrib, $sFiles
	If Not StringInStr(FileGetAttrib($sPath), "D") Then
		Return SetError(1, 0, 0)
	EndIf
	If $iSubDir = 1 Then
		$sAttrib &= ' /S'
	EndIf
	If $iSort = 1 Then
		$sAttrib &= ' /O:N'
	EndIf
	Switch $iFlag
		Case 1
			$sAttrib &= ' /A-D'
		Case 2
			$sAttrib &= ' /AD'
		Case Else
			$sAttrib &= ' /A'
	EndSwitch
	$sOut = StringToBinary('0' & @CRLF, 2)
	$sPath = StringRegExpReplace($sPath, '\\+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, '^;+|;+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, ';{2,}', ';')
	$aMasks = StringSplit($sFileMask, ';')
	For $i = 1 To $aMasks[0]
		If StringStripWS($aMasks[$i], 8) = "" Then
			ContinueLoop
		EndIf

		$sFiles &= '"' & $sPath & '\' & $aMasks[$i] & '"'

		If $i < $aMasks[0] Then
			$sFiles &= ';'
		EndIf
	Next
	$hDir = Run(@ComSpec & ' /U /C DIR ' & $sFiles & ' /B' & $sAttrib, @SystemDir, @SW_HIDE, 6)
	While ProcessExists($hDir)
		$sRead = StdoutRead($hDir, False, True)
		If @error Then
			ExitLoop
		EndIf

		If $sRead <> "" Then
			$sOut &= $sRead
		EndIf
	WEnd
	$aOut = StringRegExp(BinaryToString($sOut, 2), '[^\r\n]+', 3)
	If @error Or UBound($aOut) < 2 Then
		Return SetError(2, 0, 0)
	EndIf
	$aOut[0] = UBound($aOut) - 1
	Return $aOut
EndFunc


Добавлено:
Сообщение автоматически объединено:

если отбросить все предубеждения, то можно работать через запись в файл
Код:
$a = TimerInit()
Local $AllFiles = _FileDirList_('C:\Program Files (x86)', '*', 1); получаем строку для консоли - DIR "C:\Program Files (x86)\*" /B /S /A-D
If Not @error Then
	ConsoleWrite('_FileDirList+Файл: Время поиска - ' & TimerDiff($a) & '; Количество файлов - ' & $AllFiles[0] & @LF)
EndIf
Func _FileDirList_($sPath, $sFileMask = "*", $iFlag = 0, $iSubDir = 1, $iSort = 0)
	Local $sOutBin, $sOut, $aOut, $aMasks, $sRead, $hDir, $sAttrib, $sFiles
	If Not StringInStr(FileGetAttrib($sPath), "D") Then
		Return SetError(1, 0, 0)
	EndIf
	If $iSubDir = 1 Then
		$sAttrib &= ' /S'
	EndIf
	If $iSort = 1 Then
		$sAttrib &= ' /O:N'
	EndIf
	Switch $iFlag
		Case 1
			$sAttrib &= ' /A-D'
		Case 2
			$sAttrib &= ' /AD'
		Case Else
			$sAttrib &= ' /A'
	EndSwitch
	$sOut = StringToBinary('0' & @CRLF, 2)
	$sPath = StringRegExpReplace($sPath, '\\+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, '^;+|;+$', '')
	$sFileMask = StringRegExpReplace($sFileMask, ';{2,}', ';')
	$aMasks = StringSplit($sFileMask, ';')
	For $i = 1 To $aMasks[0]
		If StringStripWS($aMasks[$i], 8) = "" Then
			ContinueLoop
		EndIf
		$sFiles &= '"' & $sPath & '\' & $aMasks[$i] & '"'
		If $i < $aMasks[0] Then
			$sFiles &= ';'
		EndIf
	Next
	Local $tmpfile = @TempDir & '\ressearch.txt'
	If FileExists($tmpfile) Then FileDelete($tmpfile)
	$hDir = RunWait(@ComSpec & ' /U /C DIR ' & $sFiles & ' /B' & $sAttrib & ' >> ' & $tmpfile, @SystemDir, @SW_HIDE, 6)
	Local $resar = FileRead($tmpfile)
	If @error Then Return SetError(1, 0, 0)
	Local $CorrectString = StringTrimRight($resar, 2)
	Local $strspl = StringSplit($CorrectString, @LF)
	Return $strspl
EndFunc   ;==>_FileDirList_
тогда скорость получения результата становится вполне приемлемой
Код:
_FileDirList+Файл: Время поиска - 4218.2390564023; Количество файлов - 78633


Добавлено:
Сообщение автоматически объединено:

вот еще один вариант. изменены параметры чтения из консоли и формирование результирующего массива

Код:
;Изменены параметры чтения из консоли======================================================================================
$a = TimerInit()
Local $AllFiles = _FileDirList('C:\Program Files (x86)','*',1); получаем строку для консоли - DIR "C:\Program Files (x86)\*" /B /S /A-D
If Not @error Then
ConsoleWrite('_FileDirList: Время поиска - ' & TimerDiff($a) & '; Количество файлов - ' & $AllFiles[0] & @LF)
EndIf
Func _FileDirList($sPath, $sFileMask = "*", $iFlag = 0, $iSubDir = 1, $iSort = 0)
    Local $sOutBin, $sOut, $aOut, $aMasks, $sRead, $hDir, $sAttrib, $sFiles
    If Not StringInStr(FileGetAttrib($sPath), "D") Then
        Return SetError(1, 0, 0)
    EndIf
    If $iSubDir = 1 Then
        $sAttrib &= ' /S'
    EndIf
    If $iSort = 1 Then
        $sAttrib &= ' /O:N'
    EndIf
    Switch $iFlag
        Case 1
            $sAttrib &= ' /A-D'
        Case 2
            $sAttrib &= ' /AD'
        Case Else
            $sAttrib &= ' /A'
    EndSwitch
    $sOut = StringToBinary('0' & @CRLF, 2)
    $sPath = StringRegExpReplace($sPath, '\\+$', '')
    $sFileMask = StringRegExpReplace($sFileMask, '^;+|;+$', '')
    $sFileMask = StringRegExpReplace($sFileMask, ';{2,}', ';')
    $aMasks = StringSplit($sFileMask, ';')
    For $i = 1 To $aMasks[0]
        If StringStripWS($aMasks[$i], 8) = "" Then
            ContinueLoop
        EndIf
        $sFiles &= '"' & $sPath & '\' & $aMasks[$i] & '"'
        If $i < $aMasks[0] Then
            $sFiles &= ';'
        EndIf
    Next
    $hDir = Run(@ComSpec & ' /U /C DIR ' & $sFiles & ' /B' & $sAttrib, @SystemDir, @SW_HIDE, 6)
    While ProcessExists($hDir)
        $sRead = StdoutRead($hDir);изменение здесь
        If @error Then
            ExitLoop
        EndIf
        If $sRead <> "" Then
            $sOut &= $sRead
        EndIf
    Wend
	$sOut = BinaryToString($sOut, 2)
	Local $entry = StringInStr($sOut, StringLeft($sPath, 1))
	$sOut = StringTrimLeft($sOut, $entry - 1)
	$sOut = StringTrimRight($sOut, 2)
	$sOut = StringSplit($sOut, @LF)
    Return $aOut
EndFunc
Код:
_FileDirList: Время поиска - 3658.22131888479; Количество файлов - 78633

в итоге, в оригинале функция дает разные результаты поиска
поиск с использованием буфера обмена, запись файл, и с измененными параметрами чтения из консоли всегда дают точные результаты


Добавлено:
Сообщение автоматически объединено:

Хотелось бы услышать комментарии автора темы. Не для спора
 

FlatX007

Tattoo!
Сообщения
197
Репутация
35
Приветствую меня мучает эта функция .

Код:
#include <_FileDirList.au3>

$SearchUserDir = _FileDirList("d:\Game\nfs\ADDONS_5\CARS_REPLACE\", "CAR.INI", 1, 1, 0)

For $i = 1 To $SearchUserDir[0]

    ConsoleWrite($SearchUserDir [$i] & @CRLF)

Next


Этот код срабатывает один раз из 10.
Файлы в папке d:\Game\nfs\ADDONS_5\CARS_REPLACE\GTI\CAR.INI

когда сработает возвращает:
d:\Game\nfs\ADDONS_5\CARS_REPLACE\911TURBO\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\A3\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\CLK500\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\CTS\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\ECLIPSEGT\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\GTI\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\GTO\CAR.INI
d:\Game\nfs\ADDONS_5\CARS_REPLACE\IMPREZAWRX\CAR.INI


когда не срабатывает:
For $i = 1 To $SearchUserDir[0]
For $i = 1 To $SearchUserDir^ ERROR


почему массив не заполняется, файлы никуда не деваются ?!
 

joiner

Модератор
Локальный модератор
Сообщения
3,557
Репутация
628
используй другую функцию для поиска.
 

mike2003

Новичок
Сообщения
10
Репутация
0
Я попытался упростить задачу только под одни параметры. Вроде работает, но проблема, что иногда (1 раз на 10-15) выдаются иероглифы. В чем может быть дело??
LGPpoOe.jpg

Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Global $sDirectory = "t:\", $sOut, $aOut

$hGUI = GUICreate("Test", 600, 200)
$g_idMemo = GUICtrlCreateEdit("", 2, 82, 796, 596)
$ButtonRefresh = GUICtrlCreateButton("Refresh", 2, 2, 60, 30)
GUISetState(@SW_SHOW)

Read()

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $ButtonRefresh
            Refresh()
    EndSwitch
WEnd

Func Refresh()
    GUICtrlSetData($g_idMemo, "")
    Read()
EndFunc   ;==>Refresh

Func Read()
    $sOut = ""
    $aOut = ""
    $hDir = Run(@ComSpec & ' /U /C DIR "' & $sDirectory & '" /B /A-D /O-D', "", @SW_HIDE, 6)
    While ProcessExists($hDir)
        $sRead = StdoutRead($hDir, False, True)
        If @error Then
            ExitLoop
        EndIf
        If $sRead <> "" Then
            $sOut &= $sRead
        EndIf
    WEnd
    $aOut = StringRegExp(BinaryToString($sOut, 2), '[^\r\n]+', 3)
    For $i = 0 To UBound($aOut) - 1
        GUICtrlSetData($g_idMemo, $aOut[$i] & @CRLF, 1)
    Next
EndFunc   ;==>Read
 
Верх