2009-06-02 12 views
0

Я делаю простой поиск и замену в 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 

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

+0

Упорядочить по регулярному выражению, которое вы используете? –

+0

Нам нужна дополнительная информация о данных и фиксированных/переменных частях того, что вы пытаетесь найти и заменить. Все ли идентификаторы четыре цифры? Является ли «-» всегда разделителем? Вы знаете номер (1001), имя (john), оба? –

+0

Есть ли всегда два пользователя в строке, разделенные -? когда вы говорите, что разделитель сильно меняется, что вы имеете в виду? Ключ достаточно знать о разделителе, чтобы иметь возможность отличить его от данных; например «1001, foo-1000, bar-999; baz» запись foo/baz (w/separators »;» и «bar-999;») или запись bar/baz (с разделителями), foo-1000 ;" а также ";") ? – ysth

ответ

3

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

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

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

1001-mark-1001-john 

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

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

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

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

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

+0

Моя проблема в том, что я использовал (. *) Для получения чего-либо между идентификатором пользователя и «john», потому что он сильно варьируется. Но тогда, когда «знак» был первым, он, естественно, перепутался. Так как я могу обойти это? – 2009-06-02 20:45:23

3

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

#!/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-". Вероятно, лучше определить формат файла и проанализировать его, а не пытаться использовать регулярное выражение.

-1

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

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

он редактировал вопрос. поэтому мое решение, которое соответствовало его проблеме, могло идти в ногу со своими будущими изменениями. –

0

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

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

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

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

+0

Является ли этот ответ неправильным? Если вы собираетесь снизить его, пожалуйста, скажите мне, что я мог бы сделать лучше; Я не гуру регулярных выражений (как вы, возможно, догадались). –