Ответ 1
Встроенный alarm
Perl недостаточно для выхода из долгого регулярного выражения, поскольку Perl не предоставляет возможности тайм-аутов тревоги внутри внутренних кодов операций. alarm
просто не может проникнуть в него.
В некоторых случаях наиболее очевидным решением является fork
подпроцесс и время его завершения после длительного использования с помощью alarm
. Это сообщение PerlMonks демонстрирует, как отключить разветвленный процесс: Re: Timeout на script
В CPAN есть модуль Perl, называемый Sys:: SigAction, который имеет функцию под названием timeout_call
, которая прерывает длительный регулярное выражение с использованием небезопасных сигналов. Однако двигатель RE не был спроектирован так, чтобы его прерывали, и его можно оставить в неустойчивом состоянии, что может привести к ошибкам seg в 10% случаев.
Вот пример кода, который демонстрирует, что Sys:: SigAction успешно вырывается из механизма регулярных выражений, а также демонстрирует, что Perl alarm
неспособен сделать это:
use Sys::SigAction 'timeout_call';
use Time::HiRes;
sub run_re {
my $string = ('a' x 64 ) . 'b';
if( $string =~ m/(a*a*a*a*a*a*a*a*a*a*a*a*)*[^Bb]$/ ) {
print "Whoops!\n";
}
else {
print "Ok!\n";
}
}
print "Sys::SigAction::timeout_call:\n";
my $t = time();
timeout_call(2,\&run_re);
print time() - $t, " seconds.\n";
print "alarm:\n";
$t = time();
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 2;
run_re();
alarm 0;
};
if( [email protected] ) {
die unless [email protected] eq "alarm\n";
}
else {
print time() - $t, " seconds.\n";
}
Выход будет состоять из следующих строк:
$ ./mytest.pl
Sys::SigAction::timeout_call:
Complex regular subexpression recursion limit (32766) exceeded at ./mytest.pl line 11.
2 seconds.
alarm:
Complex regular subexpression recursion limit (32766) exceeded at ./mytest.pl line 11.
^C
Вы заметите, что во втором вызове - то, которое должно быть тайм-аутом с alarm
, я, наконец, должен был ctrl-C
из него, потому что alarm
был неадекватен для выхода из механизма RE.
Большое предупреждение с Sys:: SigAction заключается в том, что, хотя он способен вырваться из долговременного регулярного выражения, поскольку механизм RE не был предназначен для таких прерываний, весь процесс может стать нестабильным, что приведет к segfault. Хотя это не происходит каждый раз, это может произойти. Вероятно, это не то, что вы хотите.
Я не знаю, как выглядит ваше регулярное выражение, но если оно соответствует синтаксису, разрешенному RE2 engine, вы можете использовать модуль Perl, re:: engine:: RE2 для работы с библиотекой RE2 С++. Этот двигатель гарантирует линейный поиск времени, хотя он обеспечивает менее мощную семантику, чем встроенный движок Perl. Подход RE2 избегает всей проблемы в первую очередь, предоставляя гарантию линейного времени.
Однако, если вы не можете использовать RE2 (возможно, потому что ваша семантика регулярного выражения слишком сложна для него), метод fork/alarm, вероятно, самый безопасный способ гарантировать, что вы остаетесь в контроле.
(Кстати, этот вопрос и версия моего ответа были перекрестно настроены на PerlMonks.)