Что нового

AutoIt 3.3.6.0 - тормозят регулярные выражения

Suppir

Продвинутый
Сообщения
967
Репутация
62
Запускаю свою программу TrayGREP - а она стала в 2 - 3 раза медленней работать, чем в версии 3.3.4.0. Программа активно использует функции:
Код:
StringRegExp
StringRegExpReplace
StringSplit


Кто столкнулся с тормозами регекспов в новой версии - в чем здесь может быть дело?
 
V

VitAl2013

Гость
Re: AutoIt 3.6.6.0 - тормозят регулярные выражения

точно 3.6.6.0?
 

asdf8

Скриптер
Сообщения
564
Репутация
152
Re: AutoIt 3.6.6.0 - тормозят регулярные выражения

Проверил StringRegExp от версии 3.3 до 3.3.6
Действительно есть замедление работы от версии к версии.
Получилось, что 3.3.6 в среднем на 17% медленнее, чем 3.3

А StringReplace - наоборот, стала работать на пару процентов быстрее
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Имеется в виду свежая версия 3.3.6.0.

В моем приложении примерно 95% процессорного времени тратится на обработку текста регулярными выражениями. Замедление в несколько раз по сравнению с версией 3.3.4.0 :(
 

asdf8

Скриптер
Сообщения
564
Репутация
152
Suppir сказал(а):
в чем здесь может быть дело?

Лучше, конечно увидеть код (желательно небольшой пример), чтоб воспроизвести проблему. Угадывать можно долго, например - жадные регекспы.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Код программы:

Код:
#Include <Misc.au3>

;~ Предотвращаем запуск двух копий программы
if _Singleton("test",1) = 0 Then 
	TrayTip ("AutoIt", "TreyGREP уже запущен", 1)
	sleep(1000)
	TrayTip("clears any tray tip","",0)
	Exit
EndIf


;~ Добавляем пользовательское меню в трей
TraySetState ( 1 )
Opt("TrayMenuMode", 1)

;~ Устанавливаем хоткеи. Так как раскладка может быть и русской и английской, дублируем хоткеи для обеих раскладок
HotKeySet("^x", "_Replace")
HotKeySet("^ч", "_Replace")
HotKeySet("^+x", "_Exit")
HotKeySet("^+ч", "_Exit")

;~ Объявление переменных и массивов
Global $i, $a, $Text
Dim $Search[1000][2]
Dim $Matches, $Splited

;~ Путь к ini-файлу
$IniFile = @ScriptDir & "\" & "TrayGREP.ini"

;~ Создаем меню в трее
$Edit = TrayCreateItem("Редактировать список замен")
$Exit = TrayCreateItem("Выход")

;~ Если ini не найден...
if not FileExists($IniFile) Then
	TrayTip ("AutoIt", "Не могу найти файл ""TrayGREP.ini""", 1)
	sleep(2000)
	TrayTip("clears any tray tip","",0)
	Exit
Else	
	
	$in = FileOpen("TrayGREP.ini", 0)
	$line = FileReadLine($in, 1)
	if $line <> "TrayGREP v.1.0. Автор идеи и разработчик - Suppir." Then
		TrayTip ("AutoIt", "Информация о разработчике была повреждена", 1)
		sleep(3000)
		TrayTip("clears any tray tip","",0)
		TrayTip ("AutoIt", "Восстановите информацию или скачайте утилиту заново", 1)
		sleep(3000)
		TrayTip("clears any tray tip","",0)
		Exit
	EndIf	
EndIf	

;~ Подсказки в трее пользователю
TrayTip ("AutoIt", "TrayGREP запущен", 1)
sleep(2000)
;~ Подсказки внеобходимо убирать вручную
TrayTip("clears any tray tip","",0)

;~ Основной цикл программы
While 1 
    Switch TrayGetMsg()
	
;~ 	Если выбрали "редактировать список замен"...
    Case $Edit
;~ 		То открываем ini блокнотом. Здесь может быть проблема, если для открывания ini назначена другая программа
        ShellExecute ($IniFile)
        TrayItemSetState ( $Edit, 4 )
;~ 	Если выбрали "выйти", завершаем программу
    Case $Exit
        TrayItemSetState ( $Edit, 4 )
        _Exit()
    EndSwitch
WEnd

;~ Функция завершения программы
Func _Exit()
	
	TrayTip ("AutoIt", "Завершение работы TrayGREP", 1)
	Sleep(1000)
	Exit
	
EndFunc


;~ Функция замены текста
Func _Replace()
	
;~ 	Если в буфере обмена ничего нет, то давать предупреждение
	if ClipGet() = "" Then 
		TrayTip ("AutoIt", "Буфер пустой!", 1)
		sleep(1000)
		TrayTip("clears any tray tip","",0)

	Else
;~ 		Считываем ini-файл. Здесь может быть проблема. Если запустить программу, а потом удалить ini, то функция не будет работать
		$in = FileOpen("TrayGREP.ini", 0)
		while 1
;~ 			Читаем строки в ini последовательно
			local $i
			$line = FileReadLine($in)
			If @error = -1 Then ExitLoop
			
;~ 			Если находим строку "найти то-то", то запоминаем регексп в двумерный массив $Search[][]
;~ 			Левый элемент массива равен "найти", правый - "заменить"
			$Matches = StringRegExp($line, "^Найти:(.+)", 1)	
			If @error = 0 Then
				$Search[$i][0] = $Matches[0] 
				ContinueLoop
			EndIf	
		
;~ 			Если находим строку "Заменить на:" после которой сразу конец строки, то задаем значение переменной $Search[$i][1] = "".
;~ 			Если этого не делать, то поиск в таких случаях не будет работать.
			$Matches = StringRegExp($line, "^Заменить на:$", 1)
			If @error = 0 Then
				$Search[$i][1] = ""
				$i += 1
				ContinueLoop
			EndIf
		
;~ 			Запоминаем переменную замены
			$Matches = StringRegExp($line, "^Заменить на:(.+)", 1)
			If @error = 0 Then
				$Search[$i][1] = $Matches[0]
				$i += 1
				ContinueLoop
			EndIf
		wend	


		TrayTip ("AutoIt", "Произвожу замену...", 1)
;~ 		Берем строку из буфера
		$Text = ClipGet()
;~ 		И разрезаем ее в массив по символам конца строки. Если этого не делать, то замена будет производиться только для первого встреченного абзаца.
		$Splited = StringSplit($Text, @CRLF, 1)
		$Text = ""
		
;~ 		На каждую строку в массиве $Splited применяем регулярные выражения из массива $Search
		For $x = 1 to UBound($Splited)-1
			for $i = 0 to UBound($Search) - 1
					$Splited[$x] = StringRegExpReplace($Splited[$x], $Search[$i][0], $Search[$i][1])
			Next
;~ 			После чего склеиваем строки обратно	
			if $Text = "" Then
				$Text = $Splited[$x] 
			Else
				$Text = $Text & @CRLF & $Splited[$x] 
			EndIf
		Next

;~ 		И записываем результат в буфер обмена
		ClipPut($Text)
		TrayTip ("AutoIt", "Замена произведена", 1)
		sleep(1000)
		TrayTip("clears any tray tip","",0)

	EndIf
EndFunc


;~ необходимо добавить функции
;~ 1) объявление вспомогательных переменных в ini
;~ 2) поддержка таблиц MS Word в буфере




Примеры регулярных выражений из ini-файла:

Код:
Найти:(№|N)[ °]*(\d)
Заменить на:N $2

Найти:	+
Заменить на: 

Найти:(^)\s+
Заменить на:$1

Найти:\s+$
Заменить на:

Найти:^\s+$
Заменить на:

Найти:(\d)г\.
Заменить на:$1 г.

Найти:(\d)г 
Заменить на:$1 г. 

Найти:(\d)года
Заменить на:$1 года

Найти: (ул|г|пос|пер)\.(\S)
Заменить на: $1. $2

Найти: (п|ч|ст)\.(\d)
Заменить на: $1. $2

Найти:N°
Заменить на:N 

Найти:(\d)м 
Заменить на:$1 м 

Найти:(\d)м\. 
Заменить на:$1 м. 

Найти:""
Заменить на:"

Найти:—|–
Заменить на:-

Найти:^\s+(\d)
Заменить на:$1

Найти: (\d{4})-(\d{4}) г
Заменить на: $1 - $2 г

Найти:(\d+)\.(\D)
Заменить на:$1. $2

Найти:п р и к а з ы в а ю
Заменить на:приказываю

Найти:П Р И К А З Ы В А Ю
Заменить на:приказываю
 

asdf8

Скриптер
Сообщения
564
Репутация
152
Suppir попробуй заменить функцию:

Код:
;~ Функция замены текста
Func _Replace()
;~  Если в буфере обмена ничего нет, то давать предупреждение
    if ClipGet() = "" Or Not FileExists(@ScriptDir & "\" & "TrayGREP.ini") Then 
        TrayTip ("AutoIt", "Буфер пустой!", 1)
        sleep(1000)
        TrayTip("clears any tray tip","",0)

    Else
;~      Считываем ini-файл. Здесь может быть проблема. Если запустить программу, а потом удалить ini, то функция не будет работать
        $in = FileRead("TrayGREP.ini")
		$patern = '(Найти:.+?[\r\n]+Заменить на:[^\r\n]*)'
		$Search = StringRegExp($in, $patern, 3)
;~ 		Exit
        TrayTip ("AutoIt", "Произвожу замену...", 1)
;~      Берем строку из буфера
        $Text = ClipGet()
		If $Text Then
			If IsArray($Search) Then
				$patern = 'Найти:(.+?)[\r\n]+Заменить на:(.*)'
				For $i = 0 To UBound($Search) - 1
					$sSearch = StringRegExpReplace($Search[$i], $patern, '\1')
					$sReplace = StringRegExpReplace($Search[$i], $patern, '\2')
					ConsoleWrite('!>' & $sSearch & '-->' & $sReplace & @LF)
					$Text = StringRegExpReplace($Text, '(?m)' & $sSearch, $sReplace)
				Next
;~      	И записываем результат в буфер обмена
			ClipPut($Text)
			TrayTip ("AutoIt", "Замена произведена", 1)
			sleep(1000)
			TrayTip("clears any tray tip","",0)
			EndIf
        EndIf
    EndIf
EndFunc


Как будет по скорости?

И еще: в коде несколько раз встречается FileOpen и нету FileClose, как раз, между версиями менялась работа с файлами, мне кажется из-за этого и проблемы.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Попробую на домашнем компе (т.к. уже снес версию 3.3.6.0). Позже отпишусь.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Быстро работает, спасибо!
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Две проблемы:
1) в измененном тексте теперь вместо разделителей строк "\n" мягкие переносы строк;
2) если в тексте были пустые строки, то они исчезли.
 

asdf8

Скриптер
Сообщения
564
Репутация
152

надо подкорректировать регулярные выражения в ini-файле, ^ и $ в моем примере обозначают "\n".
Например выражение равно "\n\s+\n", а "\s" включает в себя и "\r" - вот и получаются мягкие переносы строк и удаление пустых строк ("\n\s+\n" включает "\n\r\n"). Если заменить "^\s+$" на Найти:"(^)[\t ]+([\r\n])" Заменить на:"\1\2" будет работать нормально. Ну и остальные регекспы проверить.
 
Верх