Что нового

_AU3LoadFuncList() - получение списка функций из файлов .au3

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Функция возвращает список функций (массив), которые находятся в заданном файле (.au3). По причине врожденной нелюбви к регулярным выражениям, функция _AU3LoadFuncList() написана с использованием стандартных String... функций.

Код:
#Include <Array.au3>

$List = _AU3LoadFuncList(RegRead('HKLM\SOFTWARE\AutoIt v3\AutoIt', 'InstallDir') & '\Include\WinAPI.au3')
_ArrayDisplay($List, '_AU3LoadFuncList')

Func _AU3LoadFuncList($sFile)

	Local $hFile, $Line, $Str, $Comment = 0, $Count = 0, $Error = 0
	Local $List[1] = [0]
	Local $i, $j

	$hFile = FileOpen($sFile, 0)
	If $hFile = -1 Then
		Return SetError(1, 0, $List)
	EndIf
	While 1
		$Line = FileReadLine($hFile)
		If @error Then
			Switch @error
				Case -1

				Case Else
					$Count = 0
					$Error = 1
			EndSwitch
			ExitLoop
		EndIf
		$Line = StringStripWS(StringReplace($Line, @TAB, ' '), 1)
		$i = StringInStr($Line, ' ')
		If $i > 0 Then
			$Str = StringLeft($Line, $i - 1)
		Else
			$Str = $Line
		EndIf
		Switch $Str
			Case '#comments-start', '#cs'
				$Comment += 1
			Case '#comments-end', '#ce'
				$Comment -= 1
			Case 'func'
				If $Comment <= 0 Then
					$Line = StringTrimLeft($Line, 4)
					If StringLeft($Line, 1) = ' ' Then
						$Line = StringStripWS($Line, 1)
						$i = StringInStr($Line, '(')
						$j = StringInStr($Line, ' ')
						If ($i > $j) And ($j > 0) Then
							$i = $j
						EndIf
						If $i = 0 Then
							ContinueLoop
						EndIf
						$Str = StringLeft($Line, $i - 1)
						If $Str > '' Then
							$Count += 1
							ReDim $List[$Count + 1]
							$List[$Count] = $Str
						EndIf
					EndIf
				EndIf
		EndSwitch
	WEnd
	FileClose($hFile)
	$List[0] = $Count
	ReDim $List[$Count + 1]
	Return SetError($Error, 0, $List)
EndFunc   ;==>_AU3LoadFuncList
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
А вот тоже , только через Регулярные выражения :
Код:
#Include <Array.au3>
#Include <File.au3>

$sFile=RegRead('HKLM\SOFTWARE\AutoIt v3\AutoIt', 'InstallDir') & '\Include\WinAPI.au3'

Dim $aRecords
_FileReadToArray($sFile,$aRecords)

$sList=_ArrayToString($aRecords,@CRLF ,1)

; $aList = StringRegExp ( $sList, "(^|\n)\s*Func\s*([^(]*)\(.*\)" , 3)
;$aList = StringRegExp ( $sList, "(?s)(?m)^\s*Func\s+([^\(]*)" , 3) ; Оптимизировано  SyDr
;$aList = StringRegExp ( $sList, "(?sm)^\s*Func\s*([^(]*)" , 3) 
;$aList=StringRegExp ( StringRegExpReplace ($sList,  "(?sm)(#cs|#com).*(#ce|#com.+end)(.*)" ,'\3' ), "(?i)(?sm)^\s*Func\s*([^(]*)" , 3) 
;$aList=StringRegExp ( StringRegExpReplace ($sList,"(?ims)(#cs.+#ce|#comments.+#comments-end)" ,'' ),"(?i)(?sm)^\s*Func\s*([^(]*)" , 3)
;$aList=StringRegExp ( StringRegExpReplace ($sList,  "(?ims)#[^#]+#" ,'' ), "(?ims)^\s*Func\s*([^(]*)" , 3)
$aList=StringRegExp ( StringRegExpReplace ($sList,  "(?ims)#c[^#]+#c" ,'' ), "(?ims)^\s*Func\s*([^(]*)" , 3)

_ArrayDisplay($aList,UBound($aList) & ' функций')

Причем наверняка можно и еще оптимизировать , но не с моими познаниями в РегЭкспах Рег . выражениях.

И еще один простой вариант без применения UDF ( только для _ArrayDisplay ) :
Код:
#Include <Array.au3>

$sFileFullName=RegRead('HKLM\SOFTWARE\AutoIt v3\AutoIt', 'InstallDir') & '\Include\WinAPI.au3'

$aList=AU3LoadFuncList($sFileFullName)

_ArrayDisplay($aList,UBound($aList) & ' функций')

Func AU3LoadFuncList($sFile)
   $hFile=FileOpen($sFile, 0)
   $sList=FileRead ( $hFile ) 
   
   $aArray=StringRegExp ( StringRegExpReplace ($sList,  "(?ims)#c[^#]+#c" ,'' ), "(?ims)^\s*Func\s*([^(]*)" , 3)
   
   FileClose( $hFile)
   Return  $aArray
EndFunc
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
gregaz

:smile:

Да, но есть еще #cs...#ce или #comments-start...#comments-end.



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

И что такое "Регэкспах", такого слова я не знаю.

:read_this:

:smile:
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Да, но есть еще #cs...#ce или #comments-start...#comments-end.

Это выражение проигнорирует все строки вида :
#c Func.........
#comments-start Func ........
и не включит их в массив , как и положено.


А что должно быть с комментами ???
 

SyDr

Сидра
Сообщения
651
Репутация
158
gregaz сказал(а):
А вот тоже , только через Регулярные выражения :
Причем наверняка можно и еще оптимизировать , но не с моими познаниями в Регэкспах
1) А игнорировать блоки с комментариями? :smile: Если функция находиться внутри такого блока - она не нужна
2) (^|\n) - в данном контексте это разве не одно и тоже. И зачем оно в возвращаемом массиве?
3) \(.*\) - зачем это, если список параметров не возвращается?
4) А как же func, fUnC и т.п.?

(?s)(?m)^\s*Func\s+([^\(]*)
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
SyDr сказал(а):
gregaz сказал(а):
А вот тоже , только через Регулярные выражения :
Причем наверняка можно и еще оптимизировать , но не с моими познаниями в Регэкспах
1) А игнорировать блоки с комментариями? :smile: Если функция находиться внутри такого блока - она не нужна
2) (^|\n) - в данном контексте это разве не одно и тоже. И зачем оно в возвращаемом массиве?
3) \(.*\) - зачем это, если список параметров не возвращается?
4) А как же func, fUnC и т.п.?

(?s)(?m)^\s*Func\s+([^\(]*)
Да конечно я же сказал надо оптимизировать , просмотреть все возможные варианты.
И все это запросто делается в 1-2- строках.
В этом примере я просто хотел показать насколько велико примущество Регулярных выражений перед Строковыми преобразованиями. Преклоняюсь перед создателем их.




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

gregaz [?]
Вот и твое простое решение почти всех вопросов



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

Тогда уж :
Код:
;(?s)(?m)^\s*Func\s+([^\(]*)
 (?s)(?m)^\s*Func\s*([^(]*)
 

SyDr

Сидра
Сообщения
651
Репутация
158
gregaz [?]
Да конечно я же сказал надо оптимизировать , просмотреть все возможные варианты.
И все это запросто делается в 1-2- строках.
В этом примере я просто хотел показать насколько велико примущество Регулярных выражений перед Строковыми преобразованиями. Преклоняюсь перед создателем их.
Ну... И тот, и другой код делают одно и тоже. И вполне может оказаться, что решить задачу одним способом проще и быстрее, чем другим. И ещё не ясно, что быстрее: написать через обычные строки или с использованием рег. выр.

Вот и твое простое решение почти всех вопросов
Почти :smile: Функции внутри блоков по прежнему возвращаются. Я не знаю, как оформить это в виде рег. выражения. Хотя задача может оказаться весьма близкой к другим, например:
Есть html-код. Заменить все указанные строки по указанному условию, при условиии, что данные строки находятся за пределами указанных тегов.
Весьма бы пригодилось решение такой задачи.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
SyDr [?]
Функции внутри блоков по прежнему возвращаются. Я не знаю, как оформить это в виде рег. выражения.
Покажи пример блока
Я никак не врублюсь
 

SyDr

Сидра
Сообщения
651
Репутация
158
Код:
Func _1()
EndFunc

#cs
Func _2()
EndFunc
#ce

#cs
#cs
Func _3()
EndFunc
#comments-end ;ну тут форум неправильно обр. Дальше тоже зел. должен быть.
Func _4()
EndFunc
#ce
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
IMHO одними рег. выражениями тут не обойтись.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
IMHO одними рег. выражениями тут не обойтись.
Должно получиться.
Вот похоже получил вариант для #cs - #ce : если пройдет , то остальные уже просто.
Код:
$aList=StringRegExp ( StringRegExpReplace ($sList,  "(?sm)#cs.*#ce(.*)" ,'\1' ), "(?sm)^\s*Func\s*([^(]*)" , 3)
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Осталось еще добавить условие для "FUNc" и т.д.
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Осталось еще добавить условие для "FUNc"
Это совсем просто : добавить (?i)

А это с игнорированием всех комментов все :
Код:
$aList=StringRegExp ( StringRegExpReplace ($sList,  "(?sm)(#cs|#com).*(#ce|#com.+end)(.*)" ,'\3' ), "(?i)(?sm)^\s*Func\s*([^(]*)" , 3)
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Или так :
Код:
$aList=StringRegExp ( StringRegExpReplace ($sList,"(?ims)(#cs.+#ce|#comments.+#comments-end)" ,'' ),"(?i)(?sm)^\s*Func\s*([^(]*)" , 3)

Работает ,но мне не нравится код

Вот простой код получился (и для любых комментов) :
Код:
$aList=StringRegExp ( StringRegExpReplace ($sList,  "(?ims)#c[^#]+#c" ,'' ), "(?ims)^\s*Func\s*([^(]*)" , 3)

Осталось еще "причесать" его
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,673
Репутация
2,486
amel27 на эту тему тоже как то писал функций, и я добавил к ним пару своих, вот что получилось:

Код:
#include <Array.au3>

Global $aGetAllLibs[1]
_AU3_LibGetAllUDFs(FileRead(@ScriptFullPath), $aGetAllLibs)

For $i = 1 To $aGetAllLibs[0][0]
    ConsoleWrite($aGetAllLibs[$i][0] & ":" & $aGetAllLibs[$i][2] & @CRLF)
    
	$aGetUDFs = $aGetAllLibs[$i][3]
    
	If IsArray($aGetUDFs) Then
        For $j = 0 To UBound($aGetUDFs)-1
			ConsoleWrite(@TAB & $aGetUDFs[$j] & @CRLF)
        Next
    EndIf
Next

_AU3_LibMergeAllUDFs(@ScriptDir & "\Test.au3", 0, 0, 1)

; ===============================================================
; _AU3_LibGetUnusedUDFs($sScriptFile)
; ---------------------------------------------------------------
; Возвращает список неиспользуемых в скрипте функций
; Использован AutoIT v3.3.0.0
;
; $sScriptFile   : AutoIT-скрипт для проверки
;
; При успехе  : возвращает массив неиспользуемых функций.
; При неудаче : возвращает -1 и устанавливает @error на 1 - не найден файл в переменной $sScriptFile.
;
; Автор       : G.Sandler (a.k.a CreatoR)
; ===============================================================
Func _AU3_LibGetUnusedUDFs($sScriptFile)
	If Not FileExists($sScriptFile) Then Return SetError(1, 0, -1)
	Local $aFileRead = StringSplit(StringStripCR(FileRead($sScriptFile)), @LF)
	Local $iFuncIsFounded = False, $aRetFuncsName[1]
	Local $sFuncName
	
	For $i = UBound($aFileRead) - 1 To $i = 1 Step -1
		If StringRegExp($aFileRead[$i], "^(\s+)?Func\s+") Then
			$sFuncName = StringRegExpReplace($aFileRead[$i], "(?i)Func(?:\s+)?(.*?)\(.*$", "\1")
			
			For $j = 1 To UBound($aFileRead) - 1
				$aFileRead[$j] = StringStripWS($aFileRead[$j], 3)
				If $aFileRead[$j] = "" Or StringLeft($aFileRead[$j], 1) = ";" Then ContinueLoop
				
				If Not StringRegExp($aFileRead[$j], '(?i)\bFunc\b') And _
					StringRegExp($aFileRead[$j], '(?i)' & $sFuncName & '(\(|\s+?\()|"' & $sFuncName & '"') Then
					$iFuncIsFounded = True
					ExitLoop
				EndIf
			Next
			
			If Not $iFuncIsFounded Then
				$aRetFuncsName[0] += 1
				ReDim $aRetFuncsName[$aRetFuncsName[0]+1]
				$aRetFuncsName[$aRetFuncsName[0]] = $sFuncName
			EndIf
			
			$iFuncIsFounded = False
		EndIf
	Next
	
	Return $aRetFuncsName
EndFunc ; ==> _AU3_LibGetUnusedUDFs

; ===============================================================
; _AU3_LibMergeAllUDFs($sScriptFile, $iIncludeComments=0, $iRemoveIncludes=1)
; ---------------------------------------------------------------
; Смешивает вложенные библиотеки в тело скрипта
;  (таким образом преобразовывая один цельный скрипт со всеми функциями и переменными)
; Использован AutoIT v3.2.12.1
;
; $sScriptFile        : AutoIT-скрипт для смешивания
; $iIncludeComments   : Определяет, нужно ли обрабатывать строки комментариев
; $iIncludeEmptyLines : Определяет, нужно ли обрабатывать пустые строки
; $iRemoveIncludes    : Определяет, нужно ли удалять строки #include'ов с исходного скрипта
;
; Автор               : G.Sandler (a.k.a CreatoR)
; ===============================================================
Func _AU3_LibMergeAllUDFs($sScriptFile, $iIncludeComments=0, $iIncludeEmptyLines=0, $iRemoveIncludes=1)
	Local $aGetAllLibs, $aScript_Content, $sRead_SrcScript, $sHeader_Content, $hFOpen, $iIsFuncBody = 0
	$sRead_SrcScript = FileRead($sScriptFile)
	
	_AU3_LibGetAllUDFs($sRead_SrcScript, $aGetAllLibs)
	
	If $iRemoveIncludes Then $sRead_SrcScript = StringRegExpReplace($sRead_SrcScript, "(?si)#include.*?[\r\n]+", "")
	
	For $i = $aGetAllLibs[0][0] To 1 Step -1
		$iIsFuncBody = 0
		$aScript_Content = StringSplit(StringStripCR(StringStripWS(FileRead($aGetAllLibs[$i][1]), 3)), @LF)
		
		For $j = 1 To $aScript_Content[0]
			If Not $iIncludeEmptyLines And $aScript_Content[$j] = "" Then ContinueLoop
			If Not $iIncludeComments And StringRegExp($aScript_Content[$j], "^(\s+)?;") Then ContinueLoop
			
			If StringRegExp($aScript_Content[$j], "(?i)^(\s+)?#include") Then
				ContinueLoop ;We don't need the #includes, right?
			ElseIf StringRegExp($aScript_Content[$j], "(?i)^(\s+)?Func") Then
				$sRead_SrcScript &= @CRLF
				$iIsFuncBody = 1
			ElseIf StringRegExp($aScript_Content[$j], "(?i)^(\s+)?EndFunc") Then
				$sRead_SrcScript &= $aScript_Content[$j] & @CRLF
				$iIsFuncBody = 0
				
				ContinueLoop
			EndIf
			
			If Not $iIsFuncBody Then ;Write to the Begining of file (Collecting the variables/constants Header)
				$sHeader_Content &= $aScript_Content[$j] & @CRLF
			Else ;Write to the End of file (UDF functions)
				$sRead_SrcScript &= $aScript_Content[$j] & @CRLF
			EndIf
		Next
	Next
	
	If $sHeader_Content <> "" Then $sRead_SrcScript = $sHeader_Content & @CRLF & $sRead_SrcScript
	
	$hFOpen = FileOpen(StringTrimRight($sScriptFile, 4) & "_au3lib_merged.au3", 2)
	FileWrite($hFOpen, $sRead_SrcScript)
	FileClose($hFOpen)
EndFunc ; ==> _AU3_LibMergeAllUDFs

; ===============================================================
; _AU3_LibGetAllUDFs($sScript_Content, $aIncludes_Arr)
; ---------------------------------------------------------------
; Возвращает массив всех загруженных UDF и библиотечных файлов
; Использован AutoIT v3.2.12.0
;
; $sScript_Content       : текст AutoIT-скрипта
; $aIncludes_Arr         : ссылка на переменную для выходного массива,
;                 по выходу содержит двумерный массив:
;                   $aIncludes_Arr[0][0]  - количество элементов в массиве
;                   $aIncludes_Arr[$i][0] - тип библиотечного файла
;                   $aIncludes_Arr[$i][1] - Полный путь библиотечного файла
;                   $aIncludes_Arr[$i][2] - Только имя библиотечного файла
;                   $aIncludes_Arr[$i][3] - массив UDF, определенных в файле
;
; Функция рекурсивная, поэтому выход по ошибке не предусмотрен
;
; Автор                  : amel27
; ===============================================================
Func _AU3_LibGetAllUDFs($sScript_Content, ByRef $aIncludes_Arr)
    Local Const $rFile = '(?i)(?:^|[\n\r])[ \t]*#include[ \t]+((?:\<|")[^\n\r\"\>]+(?:\>|"))'
    Local Const $rUDFs = '(?i)(?:^|[\n\r])[ \t]*Func[ \t]+([\w\d]+)'
    
	; Инициализация массива при первом входе / сохранение списка UDF
    If UBound($aIncludes_Arr, 2) <> 4 Then Dim $aIncludes_Arr[2][4] = [[1, 0, 0], [0, "", 0]]
    $aIncludes_Arr[$aIncludes_Arr[0][0]][3] = StringRegExp($sScript_Content, $rUDFs, 3)
   
	; Инициализация переменных / Получение списка библиотечных файлов
    Local $sPath, $iType, $sName, $sText
    Local $aFile = StringRegExp($sScript_Content, $rFile, 3)
   
	; Пофайловая обработка списка библиотек
	If IsArray($aFile) Then
        For $i = 0 To UBound($aFile)-1
            $sPath = _AU3_LibIncludeToPath($aFile[$i])                  ; полное имя файла
            If @error Then ContinueLoop                                 ; файл не найден
           
			$iType = @extended                                          ; тип библиотеки
            $sName = StringRegExpReplace($sPath, "(?:[^\\]+\\)+", "")   ; краткое имя файла
            
			; Исключение повторной обработки / Чтение файла
            For $j = 1 To $aIncludes_Arr[0][0]
                If $aIncludes_Arr[$j][0] == $iType And $aIncludes_Arr[$j][2] == $sName Then ContinueLoop 2
            Next
            
			$sText = FileRead($sPath)
            If @error Then ContinueLoop
            
			; При успешном чтении добавляем файл в выходной массив
            $aIncludes_Arr[0][0] += 1
            ReDim $aIncludes_Arr[$aIncludes_Arr[0][0]+1][4]
           
			$aIncludes_Arr[$aIncludes_Arr[0][0]][0] = $iType
			$aIncludes_Arr[$aIncludes_Arr[0][0]][1] = $sPath
			$aIncludes_Arr[$aIncludes_Arr[0][0]][2] = $sName
			
            ; Рекурсивный вызов на обработку текста библиотеки
            _AU3_LibGetAllUDFs($sText, $aIncludes_Arr)
        Next
    EndIf
EndFunc ; ==> _AU3_LibGetAllUDFs

; ===============================================================
; _AU3_LibIncludeToPath($sInclude)
; ---------------------------------------------------------------
; Возвращает полный путь к библиотечному файлу по строке загрузки
; Использован AutoIT v3.2.12.0
;
; $sInclude   : строка загрузки в формате #include, примеры:
;               '<array.au3>'
;               '"array.au3"'
;               '"c:\Program Files\AutoIT3\Include\array.au3"'
;
; При успехе  : возвращает полное имя файла, содержащее путь,
;               макрос @extended указывает на тип библиотеки:
;               1 - системная библиотека (каталог установки)
;               2 - текущая библиотека (каталог скрипта)
;               3 - пользовательская библиотека (путь из реестра)
;               4 - путь к библиотеке явно указан при загрузке
;
; При неудаче : возвращает пустую строку и устанавливает @error:
;               1 - ошибка формата строки
;               2 - файл не найден
;
; Автор       : amel27
; ===============================================================
Func _AU3_LibIncludeToPath($sInclude)
    Local $aRegExp = StringRegExp($sInclude, '^(<|")([^>"]+)(?:>|")$', 3)
    
	; Проверка на корректность формата строки
    If Not IsArray($aRegExp) Then Return SetError(1, 0, "")
    $sInclude = $aRegExp[1]

    If StringInStr($sInclude, "\") = 0 Then
        Local $sSYS, $sUDL, $aUDL, $sAU3 = @ScriptDir & "\" & $sInclude
        
		; Определение каталога системных библиотек
        $sSYS = StringRegExpReplace(@AutoItExe, "\\[^\\]+$", "")
        $sSYS &= "\Include\"& $sInclude
        
		; Чтение списка каталогов пользовательских библиотек
        $sUDL = RegRead("HKCU\Software\AutoIt v3\AutoIt", "Include")
        $aUDL = StringRegExp($sUDL, "([^;]+)(?:;|$)", 3)
        
		; Проверка типов 1 и 2 (до пользовательских библиотек)
        If $aRegExp[0] == '<' Then
            If FileExists($sSYS) Then Return SetError(0, 1, $sSYS)
        ElseIf $aRegExp[0] == '"' Then
            If FileExists($sAU3) Then Return SetError(0, 2, $sAU3)
        EndIf
        
		; Проверка типа 3 (поиск среди пользовательских библиотек)
        If IsArray($aUDL) Then
            For $i = 0 To UBound($aUDL)-1
                $aUDL[$i] &= "\" & $sInclude
                If FileExists($aUDL[$i]) Then Return SetError(0, 3, $aUDL[$i])
            Next
        EndIf
        
		; Проверка типов 1 и 2 (после пользовательских библиотек)
        If $aRegExp[0] == '<' Then
            If FileExists($sAU3) Then Return SetError(0, 2, $sAU3)
        ElseIf $aRegExp[0] == '"' Then
            If FileExists($sSYS) Then Return SetError(0, 1, $sSYS)
        EndIf
    Else
        ; Проверка типа 4 (файл с указанием полного пути)
        If FileExists($sInclude) Then Return SetError(0, 4, $sInclude)
	EndIf
	
    ; ОШИБКА: файл не найден
    Return SetError(2, 0, "")
EndFunc ; ==>  _AU3_LibIncludeToPath
 

XM

Знающий
Сообщения
70
Репутация
8
Подключился к развитию этой темы...
Теперь функция обрела GUI-итерфейс, добавлены некоторые опции.
Может оказаться многим полезной. Исходник прикреплен.
 
Верх