FileSearch

Everything else that doesn't fall into one of the other PB categories.
AZJIO
Addict
Addict
Posts: 1360
Joined: Sun May 14, 2017 1:48 am

FileSearch

Post by AZJIO »

I want to make a file search function for my projects. I did a non-recursive search as it is faster because I don't need to pass a bunch of parameters between recursive calls. I am currently testing on Linux. I use an array of search descriptors. The maximum nesting depth of folders is 125. If each folder is 1 character long, the path will be 250 characters.
In the future I want to add parameters. The file filter can be set not only with a regular expression. Inversion: except for those specified in the filter. The output of the full or relative paths. Before you complicate it, what do you say about the concept, maybe there is something ready-made?

FileSearch.7z - source (Windows/Linux) + program (Linux)

Image

Image

Image

Code: Select all

; DisableDebugger
EnableExplicit


CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		#sep$ = "\"
    CompilerCase #PB_OS_Linux
		#sep$ = "/"
CompilerEndSelect


Procedure __FileSearch2(sPath.s, List Files.s(), RID)
	Protected sName.s, c = 0
	Protected Dim aExaDir(125)
	Protected Dim aSePath.s(125)
	aSePath(c) = sPath
	aExaDir(c) = ExamineDirectory(#PB_Any, sPath, "*")
	If Not aExaDir(c)
		ProcedureReturn
	EndIf

	Repeat
		While NextDirectoryEntry(aExaDir(c))
			sName=DirectoryEntryName(aExaDir(c))
			If sName = "." Or sName = ".."
				Continue
			EndIf
			If DirectoryEntryType(aExaDir(c)) = #PB_DirectoryEntry_Directory ; если путь является папкой, то
				sPath = aSePath(c)
				c + 1
; 				Debug c
				aSePath(c) = sPath + sName + #sep$
; 				Debug "папка " + aSePath(c)
				aExaDir(c) = ExamineDirectory(#PB_Any, aSePath(c), "*")
; 				Debug "+" + Str(c)
				If Not aExaDir(c)
; 					Continue ; переходим к получению имени файла
; 				Else
					c - 1
; 					aSePath(c) = sPath
				EndIf
			Else
				If MatchRegularExpression(RID, sName) And AddElement(Files())
					Files() = aSePath(c) + sName
; 					Files() = aSePath(c) + "|" + sName ; удобно обрабатывать рег.выр.-ом при наличии меток в файле
; 					Debug "файл " +sName
				EndIf
			EndIf
		Wend
; 		Debug c
; 		Debug "-" + Str(c)
		FinishDirectory(aExaDir(c))
		c - 1
		
		
	Until c < 0
EndProcedure

Procedure _FileSearch(*Result.string, Path$, Mask$ = ".+?")
	Protected RID, Len, *Point
	NewList Files.s() ; список для хранения путей
	RID = CreateRegularExpression(#PB_Any, Mask$)
	If RID
		If  Right(Path$, 1) <> #sep$
			Path$ + #sep$
		EndIf
		__FileSearch2(Path$, Files.s(), RID)
		FreeRegularExpression(RID)
	Else
		ProcedureReturn
		Debug RegularExpressionError()
	EndIf
	
	Debug "Count = " + Str(ListSize(Files()))
	Len=0
	ForEach Files()
		Len + Len(Files())+2 ; вычисляем длину данных для  вмещения путей
	Next

	*Result\s = Space(Len) ; создаём строку забивая её пробелами
	*Point = @*Result\s	   ; Получаем адрес строки
	ForEach Files()
		CopyMemoryString(Files()+#CRLF$, @*Point) ; копируем очередной путь в указатель
; 		CopyMemoryString(Files(), @*Point) ; копируем очередной путь в указатель
; 		CopyMemoryString(#CRLF$, @*Point) ; копируем очередной путь в указатель
	Next

	ClearList(Files()) ; очищаем список

; 	ProcedureReturn Res
EndProcedure



Define StartTime=ElapsedMilliseconds() ; метка времени
; Path$ = "C:\Музыка\"      ; Создаёт список всех файлов и папок в домашнем каталоге.
Define Path$ = "/home/user"
; Define Path$ = "/home/user/"
														   ; Result = ""
Define Result.string ; Объявили встроенную структуру string с именем Result и элементом структуры \s
; _FileSearch(@Result, Path$, "\A.*?\.mp3\z")
; _FileSearch(@Result, Path$, "\A.*?\.js\z")
; _FileSearch(@Result, Path$, "\A.*?\.pb\z")
_FileSearch(@Result, Path$, "\A.*?\.zip\z")
; Define Res.s = "Время выполнения функции = " + Str(ElapsedMilliseconds()-StartTime) + " мсек"
Define Res.s = "Time = " + Str(ElapsedMilliseconds()-StartTime) + " ms"

; EnableDebugger

Debug Res ; вывод данных о времени выполнения
Debug "_______________"
Debug Result\s
I wanted to use a variable instead of a structure in the outer scope, as in the example below, but it didn't work.

Code: Select all

Procedure.s TrimRight(*a, n)
	Protected *p.string = @*a
	*p\s = Left(*p\s, Len(*p\s) - n)
EndProcedure

Define x.s = "Hello"
TrimRight(@x, 3)
Debug x
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: FileSearch

Post by collectordave »

Hi

Not sure if I understand exactly what you are doing but when searching for image files or other files in my apps I use a regex as below:-

This return a simple list of path/filenames from the start folder including all sub folders.

Code: Select all

Global NewList FilesFolders.s()
Global SearchFolder.s

Global SearchRegEX.i

Procedure SearchDirectory(dir$, List dList.s(), level.l = 0)
  
  Define EntryName.s
  NewList FolderList.s()
  
  Static FileCount.i

  If (level = 0)
    ClearList(dList())
  EndIf
  
  If Right(dir$, 1) <> #PS$
    dir$ + #PS$ 
  EndIf
 
  If ExamineDirectory(0, dir$, "")
    While NextDirectoryEntry(0)
      
      EntryName = DirectoryEntryName(0)     
      
      
      Select DirectoryEntryType(0)
          
        Case #PB_DirectoryEntry_Directory

          If Left(EntryName,1) <> "."  
            AddElement(FolderList())
            FolderList() = EntryName + #PS$
          EndIf

        Case #PB_DirectoryEntry_File

          If Left(EntryName,1) <> "."

            If MatchRegularExpression(SearchRegEx,EntryName)
            
              AddElement(dList())
              FileCount = FileCount + 1
              dList() = dir$ + EntryName     
            
            EndIf
            
          EndIf
        
      EndSelect
  
    Wend
  EndIf

  If ListSize(FolderList())
    ForEach FolderList()
      SearchDirectory(dir$ + FolderList(), dList(), level + 1)
    Next
  EndIf

EndProcedure

Define Word.s

;Search for all image files
;  Word = "\.(?i)(png|gif|jpg|jpeg|ico|bmp|svg|tiff)(?-i)$"

;Search for .png and .gif only
  Word = "\.(?i)(png|gif)(?-i)$"  
SearchRegEx = CreateRegularExpression(#PB_Any, Word)

;Define the topmost folder to start search
SearchFolder = GetUserDirectory(#PB_Directory_Documents) + "Images"

SearchDirectory(SearchFolder, FilesFolders())

ForEach FilesFolders()
  
  Debug FilesFolders()
Next
Maybe of some help.

CD
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
AZJIO
Addict
Addict
Posts: 1360
Joined: Sun May 14, 2017 1:48 am

Re: FileSearch

Post by AZJIO »

Search for files using a different type of mask.

Type=1 - wildcard (for example Mask$ = "*.exe")
Type=2 - listing file extensions (for example Mask$ = "pb,exe")
Type=3 - regular expression (for example Mask$ = "IMG[_\d]+.jpg")
Type=-2 - excluding the listed file extensions (for example Mask$ = "pb,exe")
Type=-3 - excluding files matching a regular expression (for example Mask$ = "IMG[_\d].jpg")

Code: Select all

EnableExplicit

; wilbert
; https://www.purebasic.fr/english/viewtopic.php?p=486382#p486382
Procedure SplitL(String.s, List StringList.s(), Separator.s = " ")
	
	Protected S.String, *S.Integer = @S
	Protected.i p, slen
	slen = Len(Separator)
	ClearList(StringList())
	
	*S\i = @String
	Repeat
		AddElement(StringList())
		p = FindString(S\s, Separator)
		StringList() = PeekS(*S\i, p - 1)
		*S\i + (p + slen - 1) << #PB_Compiler_Unicode
	Until p = 0
	*S\i = 0
	
EndProcedure

; Type
; 	1 - wildcard (for example Mask$ = "*.exe")
; 	2 - listing file extensions (for example Mask$ = "pb,exe")
; 	-2 - excluding the listed file extensions (for example Mask$ = "pb,exe")
; 	3 - regular expression (for example Mask$ = "IMG[_\d].jpg")
; 	-3 - excluding files matching a regular expression (for example Mask$ = "IMG[_\d].jpg")

; AZJIO
Procedure FileSearch(List Files.s(), sPath.s, Mask$ = "", Type = 1, depth = 130)
	Protected NewList Ext.s()
	Protected hRE, Mask2$, isNotFind

	Protected sName.s, c = 0
	Protected Dim hFind(depth)
	Protected Dim aPath.s(depth)

	Select Type
		Case 1
			Mask2$ = Mask$
		Case 2, -2
			SplitL(Mask$, Ext(), ",")
		Case 3, -3
			hRE = CreateRegularExpression(#PB_Any, Mask$, #PB_RegularExpression_NoCase)
			If Not hRE
				ProcedureReturn 2
			EndIf
	EndSelect
; 	Mask$ = CorrectMask(Mask$)

	If Right(sPath, 1) <> #PS$
		sPath + #PS$
	EndIf

	aPath(c) = sPath
	hFind(c) = ExamineDirectory(#PB_Any, sPath, Mask2$)
	If Not hFind(c)
		ProcedureReturn 1
	EndIf

	Repeat
		While NextDirectoryEntry(hFind(c))
			sName = DirectoryEntryName(hFind(c))
			If sName = "." Or sName = ".."
				Continue
			EndIf
			If DirectoryEntryType(hFind(c)) = #PB_DirectoryEntry_Directory
				If c >= depth
					Continue
				EndIf
				sPath = aPath(c)
				c + 1
				aPath(c) = sPath + sName + #PS$
				hFind(c) = ExamineDirectory(#PB_Any, aPath(c), Mask2$)
				If Not hFind(c)
					c - 1
				EndIf
			Else
				Select Type
					Case 1
						If AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
					Case 2
						ForEach Ext()
							If GetExtensionPart(sName) = Ext() And AddElement(Files())
								Files() = aPath(c) + sName
								Break
							EndIf
						Next
					Case 3
						If MatchRegularExpression(hRE, sName) And AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
					Case -3
						If Not MatchRegularExpression(hRE, sName) And AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
					Case -2
						isNotFind = 1
						ForEach Ext()
							If GetExtensionPart(sName) = Ext()
								isNotFind = 0
							EndIf
						Next
						If isNotFind And AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
				EndSelect
			EndIf
		Wend
		FinishDirectory(hFind(c))
		c - 1
	Until c < 0

	If Type = 3 Or Type = -3
		FreeRegularExpression(hRE)
	EndIf
	ProcedureReturn 0
EndProcedure

Define StartTime = ElapsedMilliseconds()
; Define Path$ = "/home/user"
Define Path$ = GetTemporaryDirectory()
Define NewList Result.s()
; FileSearch(Result(), Path$, "*.pb", 1) ; wildcard
FileSearch(Result(), Path$, "pb,exe", 2) ; listing file extensions
; FileSearch(Result(), Path$, "pb,exe", -2) ; listing file extensions
; FileSearch(Result(), Path$, ".+?\.tmp", 3) ; regular expression
; FileSearch(Result(), Path$, ".+?\.tmp", -3) ; regular expression
Define Time.s = "Time = " + Str(ElapsedMilliseconds() - StartTime) + " ms"

Debug Time
Debug "_______________"
ForEach Result()
	Debug Result()
Next
see also link

quickly convert to text

Code: Select all

Procedure ListToText(*Result.string, List StrList.s())
	Protected Len, *Point

	ForEach StrList()
		Len + Len(StrList())
	Next
	Len + ListSize(StrList()) * 2

	*Result\s = Space(Len)
	*Point = @*Result\s
	ForEach StrList()
		CopyMemoryString(StrList() + #CRLF$, @*Point)
	Next

EndProcedure

Define ResultTxt.string
ListToText(@ResultTxt, Result())
MessageRequester("", ResultTxt\s)
AZJIO
Addict
Addict
Posts: 1360
Joined: Sun May 14, 2017 1:48 am

Re: FileSearch

Post by AZJIO »

Added simulated wildcard and moved native wildcard to flag 4
Added mask correction if the user enters it in the input field and makes mistakes.

Type
0 - detect automatically. Most often 1 or 2.
1 - imitation wildcard (for example Mask$ =*.pb*|*.ico|*.exe). Use CorrectMask(Mask$) to fix user errors.
-1 - imitation of an excluding wildcard (for example Mask$ =*.pb*|*.ico|*.exe). Use CorrectMask(Mask$) to fix user errors.
2 - listing file extensions (for example Mask$ = "pb|exe"). Use CorrectMask(Mask$) to fix user errors.
-2 - excluding the listed file extensions (for example Mask$ = "pb|exe"). Use CorrectMask(Mask$) to fix user errors.
3 - regular expression (for example Mask$ = "IMG[_\d].jpg")
-3 - excluding files matching a regular expression (for example Mask$ = "IMG[_\d].jpg")
4 - native wildcard (for example Mask$ = "*.exe") only good for the current level, since the same mask is applied to the folders.

Code: Select all

EnableExplicit

; wilbert
; https://www.purebasic.fr/english/viewtopic.php?p=486382#p486382
Procedure SplitL(String.s, List StringList.s(), Separator.s = " ")
	
	Protected S.String, *S.Integer = @S
	Protected.i p, slen
	slen = Len(Separator)
	ClearList(StringList())
	
	*S\i = @String
	Repeat
		AddElement(StringList())
		p = FindString(S\s, Separator)
		StringList() = PeekS(*S\i, p - 1)
		*S\i + (p + slen - 1) << #PB_Compiler_Unicode
	Until p = 0
	*S\i = 0
	
EndProcedure

; AZJIO
Procedure SplitM(String.s, Map StringMap.s(), Separator.s = " ", CaseSensitive = 1)
    Protected S.String, *S.Integer = @S
    Protected.i p, slen

    slen = Len(Separator)
    ClearMap(StringMap())
    *S\i = @String
    Repeat
        p = FindString(S\s, Separator)
        If CaseSensitive
            AddMapElement(StringMap(), PeekS(*S\i, p - 1))
        Else
            AddMapElement(StringMap(), LCase(PeekS(*S\i, p - 1)))
            StringMap() = PeekS(*S\i, p - 1)
        EndIf

        *S\i + (p + slen - 1) << #PB_Compiler_Unicode
    Until p = 0
    *S\i = 0
EndProcedure

; AZJIO
; Выход маски "|" сигнализирует ошибку. Коррекция для типов 1 и 2
Procedure.s CorrectMask(Mask$)
	Protected rex
	Protected NewMap MaskMap.s()
; 	удалить недопустимые символы
	CompilerSelect #PB_Compiler_OS
		CompilerCase #PB_OS_Windows
			Protected InValid$ = ~"[\\\\/:\"<>]"
	    CompilerCase #PB_OS_Linux
			Protected InValid$ = "/"
	CompilerEndSelect
	rex = CreateRegularExpression(#PB_Any, InValid$, #PB_RegularExpression_NoCase)
	If MatchRegularExpression(rex, Mask$)
		FreeRegularExpression(rex)
		ProcedureReturn "|"
	EndIf
	FreeRegularExpression(rex)
; 	удалить повторяющиеся звёздочки
	While FindString(Mask$, "**")
		Mask$ = ReplaceString(Mask$, "**", "*")
	Wend
; 	удалить пробелы и точки и разделители перед разделителем
	rex = CreateRegularExpression(#PB_Any, "[\s|.]+\|", #PB_RegularExpression_NoCase)
	Mask$ = ReplaceRegularExpression(rex , Mask$ + "|" , "|")
	FreeRegularExpression(rex)
; 	Если * является частью маски, то режим искать все
	If FindString("|" + Mask$ + "|", "|*|")
		ProcedureReturn "*"
	EndIf
	Mask$ = LTrim(Mask$, "|")
	Mask$ = RTrim(Mask$, "|")
; 	Удалить дубликаты
	If FindString(Mask$, "|")
		SplitM(Mask$, MaskMap(), "|")
		Mask$ = ""
		ForEach MaskMap()
			Mask$ + MapKey(MaskMap()) + "|"
		Next
		Mask$ = RTrim(Mask$, "|")
	EndIf
	ProcedureReturn Mask$
EndProcedure

; AZJIO
Procedure IsExtMask(*text)
    Protected flag = #True, *c.Character = *text

    If *c = 0 Or *c\c = 0
        ProcedureReturn 0
    EndIf

    Repeat
        If Not ((*c\c >= 'a' And *c\c <= 'z') Or *c\c = '|' Or (*c\c >= 'A' And *c\c <= 'Z') Or (*c\c >= '0' And *c\c <= '9') Or *c\c = '_')
            flag = #False
            Break
        EndIf
        *c + SizeOf(Character)
    Until Not *c\c

    ProcedureReturn flag
EndProcedure


; Type
; 	1 - imitation wildcard (for example Mask$ =*.pb*|*.ico|*.exe). Use CorrectMask(Mask$) to fix user errors
; 	-1 - imitation of an excluding wildcard (for example Mask$ =*.pb*|*.ico|*.exe). Use CorrectMask(Mask$) to fix user errors
; 	2 - listing file extensions (for example Mask$ = "pb,exe")
; 	-2 - excluding the listed file extensions (for example Mask$ = "pb,exe")
; 	3 - regular expression (for example Mask$ = "IMG[_\d].jpg")
; 	-3 - excluding files matching a regular expression (for example Mask$ = "IMG[_\d].jpg")
; 	4 - native wildcard (for example Mask$ = "*.exe") only good for the current level, since the same mask is applied to the folders.

; AZJIO
Procedure FileSearch(List Files.s(), sPath.s, Mask$ = "", Type = 1, depth = 130)
	Protected NewList Ext.s()
	Protected hRE, Mask2$, isNotFind
	Protected *c.Character

	Protected sName.s, c = 0
	Protected Dim hFind(depth)
	Protected Dim aPath.s(depth)
	
; 	Если маска "Найти все файлы", то переключаем на производительный режим.
; 	Любой иной будет ошибкой, так как это не регулярное выражение, ни расширение, ни имитация wildcard
	If Not Asc(Mask$) Or Mask$ = "*"
		; Здесь тип меньше нуля, значит кроме всех, поэтому не ищем ничего,
		; хотя в теории пользователь ожидает найти всё и вряд ли выберет ничего не искать делая телодвижение.
		If Type < 0
			ProcedureReturn
		Else
			Type = 4
		EndIf
; 	ElseIf Mask$ = "|" ; ошибка маски при использовании CorrectMask()
; 		ProcedureReturn
	ElseIf Not Type
		If FindString(Mask$, "*") Or FindString(Mask$, "?") Or FindString(Mask$, ".")
			If depth = 0 And FindString(Mask$, "|") = 0
				Type = 4
			Else
				Type = 1
			EndIf
; 			Здесь тип 2, так как нет точки и ^\/:*?"<>|, хотя можно было бы проверить на отсутствие знаков препинания
; 		ElseIf CheckFilename(ReplaceString(Mask$, "|", ""))
		ElseIf IsExtMask(@Mask$)
			Type = 2
		Else
			Type = 3
		EndIf
	EndIf
	; 	Здесь можно сделать функцию при Type=0, то есть автоматическое обнаружение:
	; 	набор из .*? включаем тип Type=1 (релевантное решение)
	; 	набор из [a-z|] включаем тип Type=2 (релевантное решение)
	; 	иначе тип Type=3 - редкий случай, не включается автоматически
	; 	тип Type=4 - можно включить при depth=0, отсутствии "|"

	Select Type
		Case 1, -1
			If Mask$ = "|" ; ошибка маски при использовании CorrectMask()
				ProcedureReturn
			EndIf
			Mask$ = ReplaceString(Mask$, "[", "\[")
			Mask$ = ReplaceString(Mask$, "]", "\]")
			Mask$ = ReplaceString(Mask$, "$", "\$")
			Mask$ = ReplaceString(Mask$, "^", "\^")
			Mask$ = ReplaceString(Mask$, ".", "\.")
			Mask$ = ReplaceString(Mask$, "{", "\{")
			Mask$ = ReplaceString(Mask$, "}", "\}")
			Mask$ = ReplaceString(Mask$, "(", "\(")
			Mask$ = ReplaceString(Mask$, ")", "\)")
			Mask$ = ReplaceString(Mask$, "+", "\+")
			Mask$ = ReplaceString(Mask$, "?", ".")
			Mask$ = ReplaceString(Mask$, "*", ".*?")
			hRE = CreateRegularExpression(#PB_Any, "\A(" + Mask$ + ")\z", #PB_RegularExpression_NoCase)
			If Not hRE
				ProcedureReturn 2
			EndIf
			Type * 3
		Case 2, -2
			If Mask$ = "|" ; ошибка маски при использовании CorrectMask()
				ProcedureReturn
			EndIf
			SplitL(Mask$, Ext(), "|")
		Case 3, -3
			hRE = CreateRegularExpression(#PB_Any, Mask$, #PB_RegularExpression_NoCase)
			If Not hRE
				ProcedureReturn 2
			EndIf
		Case 4
			Mask2$ = Mask$
	EndSelect
; 	Mask$ = CorrectMask(Mask$)
	
	If Right(sPath, 1) <> #PS$
		sPath + #PS$
	EndIf
	
; 	Начало поиска файлов, все данные получены
	aPath(c) = sPath
	hFind(c) = ExamineDirectory(#PB_Any, sPath, Mask2$)
	If Not hFind(c)
		ProcedureReturn 1
	EndIf

	Repeat
		While NextDirectoryEntry(hFind(c))
			sName = DirectoryEntryName(hFind(c))
; 			If sName = "." Or sName = ".."
; 				Continue
; 			EndIf
			
			*c = @sName
			If *c\c = '.'
				*c + SizeOf(Character)
				If *c\c = '.'
					*c + SizeOf(Character)
					If *c\c = 0
						Continue
					EndIf
				ElseIf *c\c = 0
					Continue
				EndIf
			EndIf

			If DirectoryEntryType(hFind(c)) = #PB_DirectoryEntry_Directory
				If c >= depth
					Continue
				EndIf
				sPath = aPath(c)
				c + 1
				aPath(c) = sPath + sName + #PS$
				hFind(c) = ExamineDirectory(#PB_Any, aPath(c), Mask2$)
				If Not hFind(c)
					c - 1
				EndIf
			Else
				Select Type
					Case 4
						If AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
					Case 2
						ForEach Ext()
							If GetExtensionPart(sName) = Ext() And AddElement(Files())
								Files() = aPath(c) + sName
								Break
							EndIf
						Next
					Case 3
						If MatchRegularExpression(hRE, sName) And AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
					Case -3
						If Not MatchRegularExpression(hRE, sName) And AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
					Case -2
						isNotFind = 1
						ForEach Ext()
							If GetExtensionPart(sName) = Ext()
								isNotFind = 0
							EndIf
						Next
						If isNotFind And AddElement(Files())
							Files() = aPath(c) + sName
						EndIf
				EndSelect
			EndIf
		Wend
		FinishDirectory(hFind(c))
		c - 1
	Until c < 0

	If Type = 3 Or Type = -3
		FreeRegularExpression(hRE)
	EndIf
	ProcedureReturn 0
EndProcedure

CompilerIf #PB_Compiler_IsMainFile

Define StartTime = ElapsedMilliseconds()
; Define Path$ = "/home/user"
Define Path$ = GetTemporaryDirectory()
; Define Path$ = "C:\ProgramData\PureBasic"
Define LenForTrim = Len(Path$) + 1
Define Full = 0
Define NewList Result.s()
; FileSearch(Result(), Path$, "*.pb?", 1) ; wildcard (RegExp)
; FileSearch(Result(), Path$, "*.pb*|*.ico|*.exe|*.ini", -1) ; wildcard (RegExp)
FileSearch(Result(), Path$, "*.bin|*.toc|*.tmp|*.ini|*.pb*|*.png|*.exe|*.log|*.xml|*.ogg|*.js|*.txt", -1) ; wildcard (RegExp)
; FileSearch(Result(), Path$, "*.exe", 4) ; wildcard. Если *.exe во вложенной папки, то они не будут найдены, это проблема нативного wildcard.
; FileSearch(Result(), Path$, "pb,exe", 2) ; listing file extensions
; FileSearch(Result(), Path$, "pb,exe", -2) ; listing file extensions
; FileSearch(Result(), Path$, ".+?\.tmp", 3) ; regular expression
; FileSearch(Result(), Path$, ".+?\.tmp", -3) ; regular expression
Define Time.s = "Time = " + Str(ElapsedMilliseconds() - StartTime) + " ms"

Debug Time
Debug "_______________"
If Full
	ForEach Result()
		Debug Result()
	Next
Else
	ForEach Result()
		Debug Mid(Result(), LenForTrim)
	Next
EndIf

Debug "__________________________"
Debug "_______CorrectMask________"
Debug "1: " + CorrectMask("|*.log|*.txt   ..|*.avi..  |||*.log|***.bmp|*.log")
Debug "2: " + CorrectMask("||||")
Debug "3: " + CorrectMask("*.avi..  |*|*.log")
Debug "4: " + CorrectMask("|..|  ..  | |")
Debug "5: " + CorrectMask("*|*|*|*|")

CompilerEndIf
Last edited by AZJIO on Sun Nov 05, 2023 7:07 pm, edited 2 times in total.
User avatar
idle
Always Here
Always Here
Posts: 5095
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: FileSearch

Post by idle »

"I did a non-recursive search as it is faster"
I think you will find that it makes very little difference, as you can't make disk access any faster. The overhead of recursion is totally insignificant in this instance.
using a regex is useful though
AZJIO
Addict
Addict
Posts: 1360
Joined: Sun May 14, 2017 1:48 am

Re: FileSearch

Post by AZJIO »

Test window for searching

Code: Select all

EnableExplicit

XIncludeFile "FileSearch.pb"


; Определение языка интерфейса и применение
; Определяет язык ОС
CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		Global UserIntLang, *Lang
		If OpenLibrary(0, "kernel32.dll")
			*Lang = GetFunction(0, "GetUserDefaultUILanguage")
			If *Lang
				UserIntLang = CallFunctionFast(*Lang)
			EndIf
			CloseLibrary(0)
		EndIf
	CompilerCase #PB_OS_Linux
		Global UserIntLang$
		If ExamineEnvironmentVariables()
			While NextEnvironmentVariable()
				If Left(EnvironmentVariableName(), 4) = "LANG"
					; LANG=ru_RU.UTF-8
					; LANGUAGE=ru
					UserIntLang$ = Left(EnvironmentVariableValue(), 2)
					Break
				EndIf
			Wend
		EndIf
CompilerEndSelect


;- Lang
#CountStrLang = 18
Global Dim Lng.s(#CountStrLang)

; En
Lng(0) = "Path"
Lng(1) = "Mask"
Lng(3) = "Depth"
Lng(4) = "Full"
Lng(5) = "Result"
Lng(6) = "TypeMask"
Lng(7) = "0 - relative" + #CRLF$ + "1 - full" + #CRLF$ + "2 - filenames with extension" + #CRLF$ + "3 - filenames without extension"
Lng(8) = "0 - relative" + #CRLF$ + "1 - full"
Lng(9) = "0 - list" + #CRLF$ + "1 - array, a$(0)=number" + #CRLF$ + "2 - array"
Lng(10) = "0 - auto detect" + #CRLF$ + "1 - mask= *.is?|s*.cp*" + #CRLF$ + "2 - Type= tmp|bak|gid"
Lng(11) = "StatusBar"
Lng(12) = "Test"

Lng(13) = "Text delimited by #CRLF$"
Lng(14) = "List to enumerate"
Lng(15) = "Relative"
Lng(16) = "Full path"
Lng(17) = "File names with extension"
Lng(18) = "File names without extension"

; Ru
; если русская локализация, то русский язык


CompilerSelect #PB_Compiler_OS
	CompilerCase #PB_OS_Windows
		If UserIntLang = 1049
	CompilerCase #PB_OS_Linux
		If UserIntLang$ = "ru"
CompilerEndSelect
	Lng(0) = "Путь"
	Lng(1) = "Маска"
	Lng(3) = "Вложенность"
	Lng(4) = "Полный"
	Lng(5) = "Результат"
	Lng(6) = "Тип маски"
	Lng(7) = "0 - относительный" + #CRLF$ + "1 - полный путь" + #CRLF$ + "2 - имена файлов с расширением" + #CRLF$ + "3 - имена файлов без расширения"
	Lng(8) = "0 - относительный" + #CRLF$ + "1 - полный путь"
	Lng(9) = "0 - список" + #CRLF$ + "1 - массив с количеством элементов" + #CRLF$ + "2 - массив"
	Lng(10) = "0 - автоопределение" + #CRLF$ + "1 - маска вида *.is?|s*.cp*" + #CRLF$ + "2 - по типу tmp|bak|gid"
	Lng(11) = "Строка состояния"
	Lng(12) = "Тест"

	Lng(13) = "Текст с разделителем #CRLF$"
	Lng(14) = "Список, который перечислять"
	Lng(15) = "Относительный"
	Lng(16) = "Полный путь"
	Lng(17) = "Имена файлов с расширением"
	Lng(18) = "Имена файлов без расширения"
EndIf

;- Enumeration
Enumeration
	#InputPath
	#PathOpen
	#InputMask
	#ComboDepth
	#ComboFull
	#StatusBar
	#RadioTrue
	#RadioFalse
	#ComboArr
	#ComboTypeMask
	#ComboLocale

	#InputPathOut
	#Test
	#txt1
	#txt2
	#txt3
	#txt4
	#txt5
	#txt6
	#txt7
EndEnumeration

Global hGUI

Declare OpenFolder()
Declare Test()

;- GUI
hGUI = OpenWindow(0, 0, 0, 555, 140, "FileSearch", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)

; TextGadget(0, 10, 40, 80, 17, Lng(0))
ComboBoxGadget(#InputPath , 10, 10, 505, 30, #PB_ComboBox_Editable)
AddGadgetItem(#InputPath, -1, GetCurrentDirectory())
AddGadgetItem(#InputPath, -1, GetHomeDirectory())
AddGadgetItem(#InputPath, -1, GetTemporaryDirectory())
AddGadgetItem(#InputPath, -1, GetUserDirectory(#PB_Directory_Desktop))
AddGadgetItem(#InputPath, -1, GetUserDirectory(#PB_Directory_Downloads))
AddGadgetItem(#InputPath, -1, GetUserDirectory(#PB_Directory_Documents))
AddGadgetItem(#InputPath, -1, GetUserDirectory(#PB_Directory_ProgramData))
AddGadgetItem(#InputPath, -1, GetUserDirectory(#PB_Directory_AllUserData))
AddGadgetItem(#InputPath, -1, GetUserDirectory(#PB_Directory_Programs))
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
AddGadgetItem(#InputPath, -1, GetEnvironmentVariable("SystemRoot")) ; WinDir
AddGadgetItem(#InputPath, -1, GetEnvironmentVariable("SystemRoot") + "\System32")
CompilerEndIf
SetGadgetState(#InputPath, 2)

ButtonGadget(#PathOpen, 517, 10, 30, 30, Chr($2026))
; GUICtrlSetFont(-1, 16)
; GUICtrlSetOnEvent(-1, "OpenFolder")

; TextGadget(0, 10, 44, 105, 17, LngFunc$)
; ComboFileS$ = ComboBoxGadget("", 10, 60, 135, 23, $3)

TextGadget(#txt1, 10, 44, 150, 17, Lng(1))
ComboBoxGadget(#InputMask, 10, 70, 170, 30, #PB_ComboBox_Editable)
AddGadgetItem(#InputMask, -1, "*")
AddGadgetItem(#InputMask, -1, "*.avi|*.mpg|*.vob|*.wmv|*.mkv|*.mp4")
AddGadgetItem(#InputMask, -1, "*.mov|*.asf|*.asx|*.3gp|*.flv|*.bik")
AddGadgetItem(#InputMask, -1, "*.mp3|*.wav|*.wma|*.ogg|*.ac3")
AddGadgetItem(#InputMask, -1, "*.bak|*.gid|*.log|*.tmp")
AddGadgetItem(#InputMask, -1, "*.htm|*.html|*.css|*.js|*.php")
AddGadgetItem(#InputMask, -1, "*.bmp|*.gif|*.jpg|*.jpeg|*.png|*.tif|*.tiff")
AddGadgetItem(#InputMask, -1, "*.exe|*.msi|*.scr|*.dll|*.cpl|*.ax")
AddGadgetItem(#InputMask, -1, "*.com|*.sys|*.bat|*.cmd")
AddGadgetItem(#InputMask, -1, "avi|mpg|vob|wmv|mkv|mp4")
AddGadgetItem(#InputMask, -1, "mov|asf|asx|3gp|flv|bik")
AddGadgetItem(#InputMask, -1, "mp3|wav|wma|ogg|ac3")
AddGadgetItem(#InputMask, -1, "bak|gid|log|tmp")
AddGadgetItem(#InputMask, -1, "htm|html|css|js|php")
AddGadgetItem(#InputMask, -1, "bmp|gif|jpg|jpeg|png|tif|tiff")
AddGadgetItem(#InputMask, -1, "exe|msi|scr|dll|cpl|ax")
AddGadgetItem(#InputMask, -1, "com|sys|bat|cmd")
SetGadgetState(#InputMask, 5)

TextGadget(#txt3, 180, 44, 100, 22, Lng(3))
ComboBoxGadget(#ComboDepth, 200, 70, 55, 30)
AddGadgetItem(#ComboDepth, -1, "0")
AddGadgetItem(#ComboDepth, -1, "1")
AddGadgetItem(#ComboDepth, -1, "2")
AddGadgetItem(#ComboDepth, -1, "3")
AddGadgetItem(#ComboDepth, -1, "15")
AddGadgetItem(#ComboDepth, -1, "70")
AddGadgetItem(#ComboDepth, -1, "125")
SetGadgetState(#ComboDepth, 6)

TextGadget(#txt4, 290, 44, 60, 22, Lng(4))
ComboBoxGadget(#ComboFull, 290, 70, 50, 30)
AddGadgetItem(#ComboFull, -1, "0     " + Lng(15))
AddGadgetItem(#ComboFull, -1, "1     " + Lng(16))
AddGadgetItem(#ComboFull, -1, "2     " + Lng(17))
AddGadgetItem(#ComboFull, -1, "3     " + Lng(18))
GadgetToolTip(#ComboFull, Lng(7))
SetGadgetState(#ComboFull, 0)

TextGadget(#txt5, 370, 44, 80, 22, Lng(5))
ComboBoxGadget(#ComboArr, 380, 70, 50, 30)
AddGadgetItem(#ComboArr, -1, "0     " + Lng(13))
AddGadgetItem(#ComboArr, -1, "1     " + Lng(14))
GadgetToolTip(#ComboArr, Lng(9))
SetGadgetState(#ComboArr, 0)


TextGadget(#txt6, 460, 44, 90, 22, Lng(6))
ComboBoxGadget(#ComboTypeMask, 470, 70, 50, 30)
AddGadgetItem(#ComboTypeMask, -1, "0    = auto" )
AddGadgetItem(#ComboTypeMask, -1, "1    = *.pb*|*.ico|*.exe" )
AddGadgetItem(#ComboTypeMask, -1, "-1     <> *.pb*|*.ico|*.exe")
AddGadgetItem(#ComboTypeMask, -1, "2    = pb,exe")
AddGadgetItem(#ComboTypeMask, -1, "-2    <> pb,exe")
AddGadgetItem(#ComboTypeMask, -1, "3    = IMG[_\d].jpg")
AddGadgetItem(#ComboTypeMask, -1, "-3    <> IMG[_\d].jpg")
AddGadgetItem(#ComboTypeMask, -1, "4     wildcard")
GadgetToolTip(#ComboTypeMask, Lng(10))
SetGadgetState(#ComboTypeMask, 1)


CompilerIf #PB_Compiler_OS = #PB_OS_Windows
	SendMessage_(GadgetID(#InputMask), #CB_SETDROPPEDWIDTH, 300, 0)
	SendMessage_(GadgetID(#ComboFull), #CB_SETDROPPEDWIDTH, 300, 0)
	SendMessage_(GadgetID(#ComboArr), #CB_SETDROPPEDWIDTH, 440, 0)
	SendMessage_(GadgetID(#ComboTypeMask), #CB_SETDROPPEDWIDTH, 230, 0)
CompilerEndIf

TextGadget(#StatusBar, 5, 110, 390, 26, Lng(11))
ButtonGadget(#Test, 400, 105, 150, 33, Lng(12))
SetActiveGadget(#Test)

;- Loop
Repeat
	Select WaitWindowEvent()
		Case #PB_Event_Gadget
			Select EventGadget()
				Case #PathOpen
					OpenFolder()
				Case #Test
					Test()
			EndSelect
		Case #PB_Event_CloseWindow
			CloseWindow(0)
			End
	EndSelect
ForEver

Procedure OpenFolder()
	Protected sPath$ = GetGadgetText(#InputPath)
	If FileSize(sPath$) <> -2
		sPath$ = "C:\"
	EndIf

	sPath$ = PathRequester("", sPath$)
	If Not Asc(sPath$)
		ProcedureReturn
	EndIf
	SetGadgetText(#InputPath, sPath$)
EndProcedure

Procedure Test()
	Protected StartTime, Path0$, Mask0$, Type, depth
	Protected NewList Files.s()
	Protected LenForTrim, Full, isArr, Len, *Point, Result.String
	DisableGadget(#Test, #True)
	SetGadgetText(#StatusBar, "...")
	Path0$ = GetGadgetText(#InputPath)
	LenForTrim = Len(Path0$) + 1
	Mask0$ = GetGadgetText(#InputMask)
	Full = Val(GetGadgetText(#ComboFull))
	isArr = Val(GetGadgetText(#ComboArr))
	Type = Val(GetGadgetText(#ComboTypeMask))
	depth = Val(GetGadgetText(#ComboDepth))
	StartTime = ElapsedMilliseconds()
	Select Type
		Case 1, -1, 2, -2
			Mask0$ = CorrectMask(Mask0$)
	EndSelect
	FileSearch(Files(), Path0$, Mask0$, Type, depth)
	SetGadgetText(#StatusBar, "t=" + Str(ElapsedMilliseconds() - StartTime) + " ms,     Count = " + Str(ListSize(Files())))
	DisableGadget(#Test, #False)

	Select Full
		Case 0
			ForEach Files()
				Files() = Mid(Files(), LenForTrim)
			Next
		Case 2
			ForEach Files()
				Files() = GetFilePart(Files())
			Next
		Case 3
			ForEach Files()
				Files() = GetFilePart(Files(), #PB_FileSystem_NoExtension)
			Next
	EndSelect

	If isArr
		Debug "Count = " + Str(ListSize(Files()))
		ForEach Files()
			Debug Files()
		Next
	Else
		Len = 0
		ForEach Files()
			Len + Len(Files()) + 2
		Next

		Result\s = Space(Len)
		*Point = @Result\s
		ForEach Files()
			CopyMemoryString(Files() + #CRLF$, @*Point)
		Next
		Debug Result\s
	EndIf
EndProcedure

Post Reply