Что нового

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

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Функция возвращает список функций (массив), которые находятся в заданном файле (.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 711
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 711
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 711
Осталось еще добавить условие для "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 471
Репутация
2 401
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-итерфейс, добавлены некоторые опции.
Может оказаться многим полезной. Исходник прикреплен.
 
Верх