выровнять строку по шаблону в perl? - PullRequest
7 голосов
/ 17 ноября 2011

У меня есть кусочки строк в квадратных скобках, например:

[p1 text1/label1] [p2 text2/label2] [p3 text3/label3] [...

и т. Д.

Что внутри каждого куска не важно.Но иногда встречаются случайные куски текста, которые НЕ заключены в квадратные скобки.Например:

[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]

Я думал, что это нормально решено с помощью регулярных выражений в Perl, пока я не понял, что обслуживаю только те случаи, когда в начале, середине иликонец текста, но не там, где у нас может быть два случайных случая вместе.(как и фрагменты Y и Z выше).

Итак, я понял, что регулярные выражения в perl ловят только первый соответствующий шаблон?Как можно решить вышеуказанную проблему?

Редактировать:

Проблема заключается в том, чтобы все были заключены в квадратные скобки.Квадратные скобки никогда не бывают рекурсивными.При заключении фразы в квадратные скобки значение p зависит от значения «label».Например, если безразличная фраза без скобок

li/IN

, то она должна превратиться в:

[PP li/IN]

Я полагаю, что это микс, но единственный способ найти большее решениеПроблема, над которой я работаю, состоит в том, чтобы превратить их все в фразы в скобках, чтобы их было проще обрабатывать.Так что у меня все работает, если в начале, в середине и в конце встречается фраза без скобок, но не в том случае, если две или более встречаются вместе.

Я в основном использовал разные выражения для каждой позиции (начало, середина и конец).Тот, который ловит фразу без скобки в середине, выглядит так:

$data =~ s/\] (text)#\/label \[/\] \[selected-p-value $1#\/label\] \[/g;

Итак, я просто замечаю, что если a] до и после шаблона текста / метки, то этотнет брекетов.Я делаю нечто подобное и для других.Но я думаю, что это невероятно не универсально.Мое регулярное выражение не велико!

Ответы [ 3 ]

5 голосов
/ 17 ноября 2011
#!/usr/bin/perl

use strict;
use warnings;

my $string = "[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

# don't split inside the [], i.e. not at blanks that have p\d in front of them
my @items = split(/(?<!p\d)\s+/, $string);
my @new_items;

# modify the items that are not inside []
@new_items = map { ($_ =~ m/\[/) ? $_ :
                    ((split("/",$_))[1] eq ("IN")) ? "[PP $_]" :
                    "[BLA $_]";
                 } @items;

print join(' ', @new_items), "\n";

Это дает

[p1 text1/label1] [p2 text2/label2] [PP textX/labelX] [p3 text3/label3] [...] [PP textY/labelY] [PP textZ/labelZ] [...]

Я понял, что PP имел в виду, как я использовал это здесь, иначе map придется немного усложнить.

РЕДАКТИРОВАТЬ

Я отредактировал код в ответ на ваш комментарий.Если вы используете

"[p1 text1/label1] [p2 text2/label2] textX/IN  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

в качестве образца строки, это будет вывод:

[p1 text1/label1] [p2 text2/label2] [PP textX/IN] [p3 text3/label3] [...] [BLA textY/labelY] [BLA textZ/labelZ] [...]

Только одна вещь, которую нужно иметь в виду: регулярное выражение, используемое с split, не будет работать дляpn при n> 9. Если у вас есть такие случаи, лучше всего искать альтернативу, потому что задние части переменной длины не были реализованы (или, по крайней мере, в моей версии Perl (5.10.1) они не реализованы).

РЕДАКТИРОВАТЬ 2

В качестве ответа на ваш второй комментарий, вот модифицированная версия скрипта.Вы обнаружите, что я также добавил что-то в пример строки, чтобы продемонстрировать, что она теперь работает, даже если внутри [...].

#!/usr/bin/perl

use strict;
use warnings;

my $string = "[p1 text1/label1] [p2 text2/label2] textX/IN  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...] xyx/IN [opq rs/abc]";

# we're using a non-greedy match to only capture the contents of one set of [], 
# otherwise we'd simply match everything between the first [ and the last ].
# The parentheses around the match ensure that our delimiter is KEPT.
my @items = split(/(\[.+?\])/, $string);

#print "..$_--\n" for @items;  # uncomment this to see what the split result looks like

# modify the items that are not inside []
my @new_items = map {
                     if (/^\[/) { # items in []
                        $_;
                     }
                     elsif (/(?: \w)|(?:\w )/) { # an arbitrary number of items without []
                       my @new =  map { ($_ =~ m/\[/) ? $_ :
                                        ((split("/",$_))[1] eq ("IN")) ? "[PP $_]" :
                                        "[BLA $_]";
                                      } split;
                     }
                     else { # some items are '', let's just discard those
                     }
                    } @items;

print join(' ', @new_items), "\n";

нет *1031* Вывод такой:

[p1 text1/label1] [p2 text2/label2] [PP textX/IN] [p3 text3/label3] [...] [BLA textY/labelY] [BLA textZ/labelZ] [...] [PP xyx/IN] [opq rs/abc]

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

2 голосов
/ 17 ноября 2011

На самом деле вы можете решить эту проблему, используя "only" regex:

#!/usr/bin/perl

use strict;
use warnings;

$_ = "[p1 text1/label1] [p2 text2/label2] textX/labelX  [p3 text3/label3] [...] textY/labelY textZ/labelZ [...]";

s{ ([^\s[]+)|(\[(?:[^[]*)\])     }
 { if( defined $2){ $2 } elsif(defined $1)
    { 
       if($1 =~ m!(.*(?<=/)(.*))!)
       {
         if($2 eq 'labelX')
         {
            "[PP $1]";
         }
         elsif($2 eq 'labelY')
         {
            "[BLA $1]";
         }
         elsif($2 eq 'labelZ')
         {
            "[FOO $1]";
         }
       }
    }
 }xge;

 print;

Выход:

[p1 text1/label1] [p2 text2/label2] [PP textX/labelX]  [p3 text3/label3] [...] [BLA textY/labelY] [FOO textZ/labelZ] [...]
2 голосов
/ 17 ноября 2011

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

my $teststring = "hello world";

$teststring =~ s/o/X/;

станет hellX world. но

$teststring =~ s/o/X/g;

станет hellX wXrld, заметив все совпадения.

Я думаю, что ваша проблема похожа на

my $teststring = ' A B C ';

$teststring =~ s/\s(\w)\s/ [$1] /ig;

выход [A] B [C]. Это не делает B, и причина в том, что как часть сопоставления A механизм регулярных выражений также занимал пространство после A. И в оставшейся строке нет места перед B, поэтому он не совпадает.

Но если вы делаете не жадный матч, как это

$teststring =~ s/\s(\w)\s*?/ [$1] /ig;

это дает [A] [B] [C]

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