В этих ситуациях я бы использовал go для синтаксического анализа. Таким образом, вам не нужно придумывать регулярное выражение, которое делает несколько разных вещей. Это важно, поскольку сложность строки меняется. Несмотря на то, что это выглядит как дополнительный код, это базовый c Perl, и вы помещаете его в подпрограмму. Я могу легко добавить другой тип токена, не нарушая механику кода или не переписывая шаблон. Я также использовал этот трюк в Как мне получить неизвестное количество захватов из шаблона? :
use v5.10;
my $string = 'for element in hydrogen helium "carbon 14" $(some stuff "here") FILE';
# The types of things you can match, going from most specific
# to least specific. Now you only need to describe what each
# individual thing looks like. Each pattern is responsible for
# the capture group $1, which is the thing we'll save.
my @patterns = (
qr/ ( \$\( .+? \) ) /x,
qr/ ( " .+? " ) /x,
qr/ ( \S+ ) /x,
);
my @tokens;
# The magic is global matching in scalar context,
# using /g. The \G anchor starts matching at the
# last position you matched in the prior match of
# the same string (that's in pos()). Normally that
# position is reset when a match fails, but /c
# prevents that so you can try other patterns. Once
# you match a pattern, save what you matched and
# move on.
#
# The pattern here also takes care of trailing whitespace.
while( pos($string) < length($string) ) {
foreach my $pattern ( @patterns ) {
next unless $string =~ m/ \G $pattern \s*/gcx;
push @tokens, $1;
last;
}
}
use Data::Dumper;
say Dumper( \@tokens );
Вы можете сделать то же самое с оператором сброса ветки для каждого захвата в чередовании: $1
:
use v5.10;
my $string = 'for element in hydrogen helium "carbon 14" $(some stuff "here") FILE';
my @tokens = $string =~ m/
(?|
(?: ( \$ \( .+? \) ) ) |
(?: ( " .+? " ) ) |
(?: ( \S+ ) )
)
/gx;
use Data::Dumper;
say Dumper( \@tokens );
Это немного сложнее, чем ответ zdim , но он гораздо более гибкий. Скажем, например, вы решили, что вам не нужны кавычки около "carbon 14"
. Это очень легко исправить, потому что структура регулярного выражения не меняется. Вы изменяете только тот подшаблон, который работает с этим токеном:
(?|
(?: ( \$ \( .+? \) ) ) |
(?: " ( .+? ) " ) |
(?: ( \S+ ) )
)
Эта дополнительная гибкость может не понадобиться. Обычно я сталкиваюсь с дополнительными странными ситуациями при выполнении таких задач, поэтому я начинаю с гибкого решения. Это не имеет большого значения после того, как вы сделали это пару раз.
Что касается вашей ошибки, вы получили:
Lookbehind дольше 255 не реализовано в регулярном выражении.
До версии 5.30 вы не могли иметь просмотр назад с переменной шириной . Теперь это экспериментальная функция, но шаблон должен знать заранее, что он не go более 255 символов. В вашем шаблоне (?<=\"[^\"]*\")
, а *
- ноль или больше. Это значение может быть больше 255, поэтому это недопустимый шаблон.
regexr.com использует PCRE, который раньше обозначал «Perl совместимый», но они разошлись настолько, что некоторые вещи, которые выглядят как они может работать нормально на других языках, но не работать в Perl. Обычно это не проблема, но ретроспективный просмотр - одно из отличий.