Нужна помощь с жадным квантификатором - PullRequest
0 голосов
/ 03 июня 2009

Я делаю простой поиск и замену в Perl, но мне нужна помощь. Это строки в файле:

1001(seperator could be "anything")john-1001(seperator could be "anything")mark
1001(seperator could be "anything")mark-1001(seperator could be "anything")john

Я хочу назначить новый идентификатор пользователя для Джона, как 2001. Так что это результат, который я хочу:

2001($1)john-1001-mark
1001-mark-2001($1)john

Мое регулярное выражение работает хорошо, когда Джон первый, но когда Марк первый, он испортился.

Ответы [ 4 ]

3 голосов
/ 03 июня 2009

Попробуйте это:

#!/usr/bin/perl

use strict;
use warnings;

while (<DATA>) {
    s/\b1001-john\b/2001-john/;
    print;
}

__DATA__
1001-john-1001-mark
1001-mark-1001-john
11001-john
1001-johnny

\b не позволяет ему сопоставлять что-либо, кроме "1001-john". См. Раздел «Утверждения» в perldoc perlre для получения дополнительной информации.


Хммм, звучит так, будто тебе нужен sexeger :

#!/usr/bin/perl

use strict;
use warnings;

while (<DATA>) {
    my $s = reverse;
    $s =~ s/\bnhoj(.*?)1001\b/nhoj${1}1002/;
    $s = reverse $s;
    print $s;
}

__DATA__
1001-john-1001-mark
1001-mark-1001-john
11001-john
1001-johnny

Основная идея sexeger - перевернуть строку, использовать обратное регулярное выражение, а затем перевернуть результат. Проблема в том, что .*? дает вам самую короткую строку из первого совпадения, а не самую короткую из возможных строк. Конечно, это все еще будет иметь проблему с "1001-mark-2001-john", так как .*? будет соответствовать "-mark-2001-". Вероятно, лучше определить формат файла и проанализировать его, а не пытаться использовать регулярное выражение.

3 голосов
/ 03 июня 2009

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

s/\b1001\b(?=.*?\bjohn\b)/2001/

Заменяет «1001», когда следует «Джон», при совпадении с минимальным количеством промежуточных символов. .*? - это не жадная версия .*. Однако регулярные выражения всегда совпадают, если это возможно, поэтому это все равно будет соответствовать

1001-mark-1001-john

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

  • Символы, которые разделитель может содержать.
  • Символы, которые разделитель не может содержать .
  • Количество символов в разделителе.

Если предположить, что разделитель не может содержать символы «слова» (a-z, 0-9 и подчеркивание), мы можем получить что-то работающее:

s/\b1001\b(?=\W+?\bjohn\b)/2001/

Известные части ("1001" и "john") ограничены, чтобы помешать им сопоставлять другие строки с этими подстроками. (Спасибо Часу за то, что он заметил этот крайний случай.)

0 голосов
/ 03 июня 2009

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

В этом случае попробуйте:

s/\d+([^\d]*)john/2001$1john/

Это сохранит разделитель между "1001" и "john" во время замены. Обратите внимание, что в разделителе недопустимы цифры, поэтому это будет работать, даже если после «mark» появляется «john» (поскольку «-mark-1001-» не является допустимым разделителем).

0 голосов
/ 03 июня 2009

это может быть что-то вроде

$s = '1001-mark-1001-john';
$s =~ s/(\d+)(-john)/2001$2/i;
print $s;
...