Каков наилучший способ применения многих тестов регулярных выражений Perl? - PullRequest
1 голос
/ 23 октября 2010

У меня есть модуль Perl, который сопоставляет текст со списком сотен регулярных выражений; в настоящее время я их просто использую:

if (
  /?:re1/ or
  ...
  /re200$/
) { return "blah"; }

Есть ли лучший / быстрый / менее ресурсоемкий способ сделать это? Возможно, существует полезный модуль, или я должен хранить их в хэше и т. Д.

Ответы [ 4 ]

10 голосов
/ 23 октября 2010

Взгляните на Regexp::Assemble.

Это из описания:

Regexp :: Assemble берет произвольное количество регулярных выражений и собирает их в одно регулярное выражение (или RE), которое соответствует всем, что совпадают отдельные RE.

В результате вместо того, чтобы иметь большой список выражений для зацикливания, целевая строка должна проверяться только с одним выражением. Это интересно, когда вам приходится иметь дело с несколькими тысячами паттернов. Прилагаются серьезные усилия для создания наименьшего возможного рисунка.

Можно также отслеживать исходные шаблоны, чтобы вы могли определить, какой из исходных шаблонов, образующих собранный шаблон, был тем, который вызвал совпадение.

Я использовал его для некоторых проектов, и это было довольно удивительно.

7 голосов
/ 23 октября 2010

Из ответа perlfaq6 на Как эффективно сопоставить много регулярных выражений одновременно?


Как эффективно сопоставить много регулярных выражений одновременно?

(предоставлено Брайаном Д. Фой)

Если у вас Perl 5.10 или новее, это почти тривиально. Вы просто сопоставляете массив объектов регулярных выражений:

my @patterns = ( qr/Fr.d/, qr/B.rn.y/, qr/W.lm./ );

if( $string ~~ @patterns ) {
    ...
    };

Интеллектуальное совпадение прекращается, когда оно находит совпадение, поэтому не нужно пробовать каждое выражение.

Раньше, чем Perl 5.10, у вас есть немного работы. Вы хотите избегать компиляции регулярного выражения каждый раз, когда хотите его сопоставить. В этом примере perl должен перекомпилировать регулярное выражение для каждой итерации цикла foreach, поскольку он не может знать, каким будет $ pattern:

my @patterns = qw( foo bar baz );

LINE: while( <DATA> ) {
    foreach $pattern ( @patterns ) {
        if( /\b$pattern\b/i ) {
            print;
            next LINE;
            }
        }
    }

Оператор qr // появился в perl 5.005. Он компилирует регулярное выражение, но не применяет его. Когда вы используете предварительно скомпилированную версию регулярного выражения, Perl выполняет меньше работы. В этом примере я вставил карту, чтобы превратить каждый шаблон в предварительно скомпилированную форму. Остальная часть скрипта такая же, но быстрее:

my @patterns = map { qr/\b$_\b/i } qw( foo bar baz );

LINE: while( <> ) {
    foreach $pattern ( @patterns ) {
        if( /$pattern/ )
            {
            print;
            next LINE;
            }
        }
    }

В некоторых случаях вы можете сделать несколько шаблонов одним регулярным выражением. Остерегайтесь ситуаций, которые требуют возврата назад.

my $regex = join '|', qw( foo bar baz );

LINE: while( <> ) {
    print if /\b(?:$regex)\b/i;
    }

Подробнее об эффективности регулярных выражений см. В статье «Освоение регулярных выражений» Джеффри Фрейдля. Он объясняет, как работает механизм регулярных выражений и почему некоторые шаблоны удивительно неэффективны. Как только вы поймете, как Perl применяет регулярные выражения, вы можете настроить их для отдельных ситуаций.

1 голос
/ 23 октября 2010

Если ваши регулярные выражения не меняются, вам следует использовать суффикс / o, который потребует их компиляции только один раз. Это очень поможет скорости.

Кроме того, упорядочите их по «наиболее распространенному отклонению первым» или «по первому наименьшему количеству подстановочных знаков», чтобы последующие, более ресурсоемкие, выполнялись реже.

Конечно, можно прочитать их из файла, но правильный способ сделать это для скорости - это прочитать их, а затем создать подпрограмму внутри текстовой строки, а затем вычислить текстовую строку, чтобы вернуть анонимную подпрограмму для вызова. , Это дает вам преимущество в скорости в жестко запрограммированном списке регулярных выражений с гибкостью списка переменных регулярных выражений.

1 голос
/ 23 октября 2010

Вы можете хранить свои регулярные выражения в отдельном файле, по одному в строке.Он не будет работать быстрее, но код будет намного чище.

Прочитайте их с помощью чего-то вроде

my @patterns;
while (<>) {
    push @patterns, qr/$_/;
}

Для повышения производительности мой единственный совет - использовать настоящий парсер. ANTLR цель perl кажется несуществующей, но у меня не было проблем с использованием синтаксического анализатора, сгенерированного в Java с помощью perl Inline::Java.

Edit: подумал еще об одной незначительной вещи: Заказатьрегулярные выражения от наиболее часто совпадающих до наименее часто совпадающих.Может дать вам небольшое улучшение постоянного фактора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...