помочь с правилами Perl Regex - PullRequest
       0

помочь с правилами Perl Regex

3 голосов
/ 25 сентября 2011

Мне нужна помощь с проблемой регулярных выражений в Perl.Мне нужно сопоставить символы non_letter, «зародившиеся» вокруг строки буквенных символов (размером один).

То есть ... У меня есть строка типа

CDF((E)TR)FT

, и я хочусопоставьте ВСЕ следующие:

C, D, F((, ((E), )T, R), )F, T.

Я пытался что-то вроде

/([^A-Za-z]*[A-Za-z]{1}[^A-Za-z]*)/

, но я получаю:

C, D, F((, E), T, R), F, T.

Это как если бы не-буквенные символы были сопоставлены, и они НЕ могут быть сопоставлены в другом сопоставлении.

Как я могу это сделать?

Ответы [ 4 ]

4 голосов
/ 26 сентября 2011

Немного опоздал на это. Возможно, кто-то уже предлагал это.

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

Класс символов упрощен для ясности:
/(?=([^A-Z]*))(\1[A-Z])(?=([^A-Z]*))/

(?=([^A-Z]*)) # вперед - необязательные символы, отличные от A-Z, записанные в grp 1
(\1[A-Z]) # захватить группу 2, использовать группу захвата 1, плюс атомную букву
(?=([^A-Z]*)) # вперед - необязательные символы не A-Z, записанные в grp 3

Глобально, в цикле while, объединенные группы $2$3 (в таком порядке) являются ответом.

Тест:

$samp = 'CDF((E)TR)FT';

while ( $samp =~ /(?=([^A-Z]*))(\1[A-Z])(?=([^A-Z]*))/g )
{
   print "$2$3, ";
}
* * Тысяча двадцать-одина выход:

C, D, F((, ((E), )T, R), )F, T,

2 голосов
/ 25 сентября 2011

Проблема в том, что вы потребляете свои символы или не буквенные символы при первом обращении к ним, поэтому вы не можете сопоставить все, что хотите.Решением было бы использование разных регулярных выражений для разных шаблонов и объединение результатов в конце, чтобы вы могли получить желаемый результат:

Это будет соответствовать всем символам, начинающимся с не символа, за которым следует один символ, но НЕсопровождаемый не символом

[^A-Z]+[A-Z](?![^A-Z])

Это будет соответствовать символу, заключенному в не символы, содержащие перекрывающиеся результаты:

(?=([^A-Z]+[A-Z][^A-Z]+))

Это будет соответствовать символу, за которым следует только один или несколько не символовесли ему не предшествует не символ:

(?<![^A-Z])[A-Z][^A-Z]+

И это будет соответствовать отдельным символам, которые не заключены в не символы

(?<![^A-Z])[A-Z](?![^A-Z])

Комбинируя результаты, вы получите исправить желаемый результат :

C,D,T, )T, )F, ((E), F((, R)

Также, если вы понимаете мелкие детали, вы можете объединить это в одно регулярное выражение:

#!/usr/local/bin/perl

use strict;

my $subject = "0C0CC(R)CC(L)C0";

while ($subject =~ m/(?=([^A-Z]+[A-Z][^A-Z]+))|(?=((?<![^A-Z])[A-Z][^A-Z]+))|(?=((?<![^A-Z])[A-Z](?![^A-Z])))|(?=([^A-Z]+[A-Z](?![^A-Z])))/g) {
# matched text = $1, $2, $3, $4
print $1, " " if defined $1;
print $2, " " if defined $2;
print $3, " " if defined $3;
print $4, " " if defined $4;
}

Вывод:

0C0 0C C( (R) )C C( (L) )C0
1 голос
/ 25 сентября 2011

Или, вы можете сделать это сложным способом и сначала токенизировать, а затем обработать токены:

#!/usr/bin/perl
use warnings;
use strict;

my $str = 'CDF((E)TR)FT';
my @nucleated = nucleat($str);
print "$_\n" for @nucleated;

sub nucleat {
    my($s) = @_;
    my @parts;   # return list stored here

    my @tokens = grep length, split /([a-z])/i, $s;

    # bracket the tokens with empty strings to avoid warnings
    unshift @tokens, '';
    push @tokens, '';

    foreach my $i (0..$#tokens) {
        next unless $tokens[$i] =~ /^[a-z]$/i; # one element per letter token       
        my $str = '';

        if ($tokens[$i-1] !~ /^[a-z]$/i) { # punc before letter
            $str .= $tokens[$i-1];
        }

        $str .= $tokens[$i];               # the letter

        if ($tokens[$i+1] !~ /^[a-z]$/i) { # punc after letter
            $str .= $tokens[$i+1];
        }

        push @parts, $str;
    }

    return @parts;
}
1 голос
/ 25 сентября 2011

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

(?<=(\P{L}*))\p{L}(?=(\P{L}*))

, где результатом совпадения будет буква, а $1 и $2 будут содержать не буквы вокруг него. Поскольку они сопоставляются только в контексте повторяющихся утверждений, они не используются в сопоставлении и поэтому могут сопоставляться несколько раз. Затем вам нужно построить результат совпадения как $1 + $& + $2. Этот подход будет работать в .NET, например.

В большинстве других разновидностей (включая Perl), которые имеют ограниченную поддержку lookaround, вы можете использовать смешанный подход, который необходим, потому что выражения lookbehind не допускают неопределенного повторения:

\P{L}*\p{L}(?=(\P{L}*))

Теперь $& будет содержать небуквенные символы перед буквой и саму букву, а $1 содержит любые небуквенные символы, следующие за буквой.

while ($subject =~ m/\P{L}*\p{L}(?=(\P{L}*))/g) {
    # matched text = $& . $1
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...