2013-03-08 3 views
4

Я изучаю тайну regexp. Я устал, поэтому мне может не хватать что-то очевидное - но я не вижу причин для этого.Регулярное выражение mystery

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

Предположим, что мы имеем на данный файл:

$ cat data 
1 =2 3 =4 
5 =6 7 =8 

Мы можем затем удалить пробелы в передней части «=» с ...

$ cat data | perl -ne 's,(.)\s+=(.),\1=\2,g; print;' 
1=2 3=4 
5=6 7=8 

Обратите внимание, что в каждой строке, все экземпляры матча заменяются; мы использовали модификатор/g search, который не останавливается при первой замене, и вместо этого заменяется до конца строки.

Например, было удалено пространство перед «= 2» и пространство перед ; «= 4»; в той же строке.

Почему бы не использовать более простые конструкции типа 's, =, =, g'? Ну, мы были подготовка к более сложных сценариев ... где правая из заданий цитируются строки, и может быть либо одинарные или двойные кавычки:

$ cat data2 
1 ="2" 3 ='4 =' 
5 ='6' 7 ="8" 

Чтобы сделать ту же работу (удалить пробел перед знаком равенства), мы должны быть осторожными, поскольку строки могут содержать равные знака - так мы отмечаем первую цитату мы видим, и искать его через обратные ссылки:

$ cat data2 | perl -ne 's,(.)\s+=(.)([^\2]*)\2,\1=\2\3\2,g; print;' 
1="2" 3='4 =' 
5='6' 7="8" 

Мы использовал обратную ссылку \ 2 для поиска чего-либо, что не является той же цитатой, что и первая, которую мы видели в любое время ([^ \ 2] *). Затем мы выполнили поиск самой оригинальной цитаты (\ 2). Если найдено, , мы использовали обратные ссылки для ссылки на согласованные детали в замене цели .

Теперь посмотрите на это:

$ cat data3 
posAndWidth ="40:5 =" height  ="1" 
posAndWidth ="-1:8 ='" textAlignment ="Right" 

То, что мы хотим здесь, чтобы уронить последний пробел, который существует перед тем все экземпляры «=» в каждой строке. Как и раньше, мы не можем использовать простой 's, = ", =", g', потому что сами строки могут содержать знак .

Таким образом, мы по той же схеме, как мы делали выше, и использовать обратные ссылки:

$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,g; print;" 
posAndWidth="40:5 =" height  ="1" 
posAndWidth="-1:8 ='" textAlignment ="Right" 

Он работает ... но только на первый матч линии! Пространство, следующее за «textAlignment», не было удалено, и ни один из них не был («высота»).

В основном, это, кажется, что/г не работает больше: под управлением той же команду заменить без/г производит точно такой же вывод:

$ cat data3 | perl -ne "s,(\w+)(\s*) =(['\"])([^\3]*)\3,\1\2=\3\4\3,; print;" 
posAndWidth="40:5 =" height  ="1" 
posAndWidth="-1:8 ='" textAlignment ="Right" 

Оказывается, что в этом регулярном выражении, то/г игнорируется , Любые идеи, почему?

+2

Разве это не лечащий все между первой цитаты и последней цитаты в кавычках? – Nick

+1

Часть [^ \ 3] * не может идти за пределами закрывающей цитаты, не так ли? – ttsiodras

+0

с вашим perl cmd, я получил другой результат 'posAndWidth =" 40: 5 = "' пробел между '5' и' = 'ушел. – Kent

ответ

1

Я остановлюсь на мой комментарий к ответу TLP в:

ttsiodras вас просят два вопроса:

1- почему ваше регулярное выражение не производят желаемого результата? почему флаг g не работает?

Ответ заключается в том, что ваше регулярное выражение содержит эту часть [^\3], которая не обрабатывается правильно: \3 не распознается как обратная ссылка. Я искал его, но не мог найти способ иметь обратную ссылку в классе символов.

2- как вы удаляете пространство перед знаком равенства и оставляете в покое часть, которая приходит после и находится между кавычками?

Это будет способ сделать это (см this reference):

$ cat data3 | perl -pe "s,(([\"']).*?\2)| (=),\1\3,g" 
posAndWidth="40:5 =" height  ="1" 
posAndWidth="-1:8 ='" textAlignment="Right" 

1-ая часть регулярных выражений уловов все, что в кавычки (одинарные или двойные) и заменяется на матч, вторая часть соответствует знаку равенства, которому предшествует пространство, которое вы ищете. Обратите внимание, что это решение является лишь работа по «интересной» части о дополнении класса символов оператора с задней ссылкой [^\3] с помощью нежадным оператора *?


Наконец, если вы хотите продолжить на negative lookahead solution:

$ cat data3 | perl -pe 's,(\w+)(\s*) =(["'"'"'])((?:(?!\3).)*)\3,\1\2=\3\4\3,g' 
posAndWidth="40:5 =" height  ="1" 
posAndWidth="-1:8 ='" textAlignment="Right" 

часть с кавычками в квадратных скобках все еще означает "[\"']", но я должен был использовать одиночные кавычки вокруг всей команды PERL в противном случае отрицательного л Синтаксис ookahead (?!...) возвращает ошибку в bash.

EDIT Исправленный регулярное выражение с отрицательным предпросмотром: обратите внимание на нежадный оператор *? снова и g флага.

EDIT Взял комментарий ttsiodras: убрал неживого оператора.

EDIT Принимал комментарий TLP в расчет

+0

Вторая часть вашего ответа (отрицательная обратная ссылка, с которой я начиналась) не работает - она ​​удаляет только пространство первого знака равенства ... – ttsiodras

+0

Это правда, мне нужно посмотреть дальше. – cooltea

+0

Хорошо, я исправил второе регулярное выражение, подумал, что это займет у меня больше времени. – cooltea

3

Вставка некоторые символы отладки в вашем замещением проливает некоторый свет на вопрос:

use strict; 
use warnings; 

while (<DATA>) { 
    s,(\w+)(\s*) =(['"])([^\3]*)\3,$1$2=$3<$4>$3,g; 
    print;      # here -^ -^ 
} 

__DATA__ 
posAndWidth ="40:5 =" height  ="1" 
posAndWidth ="-1:8 ='" textAlignment ="Right" 

Выход:

posAndWidth="<40:5 =" height  ="1>" 
posAndWidth="<-1:8 ='" textAlignment ="Right>" 
#   ^--------- match ---------------^ 

Обратите внимание, что матч проходит через обе котировки сразу. Казалось бы, [^\3]* не делает то, что вы думаете.

Regex - не лучший инструмент здесь. Используйте анализатор, который может обрабатывать строки в кавычках, например Text::ParseWords:

use strict; 
use warnings; 
use Data::Dumper; 
use Text::ParseWords; 

while (<DATA>) { 
    chomp; 
    my @a = quotewords('\s+', 1, $_); 
    print Dumper \@a; 
    print "@a\n"; 
} 

__DATA__ 
posAndWidth ="40:5 =" height  ="1" 
posAndWidth ="-1:8 ='" textAlignment ="Right" 

Выход:

$VAR1 = [ 
      'posAndWidth', 
      '="40:5 ="', 
      'height', 
      '="1"' 
     ]; 
posAndWidth ="40:5 =" height ="1" 
$VAR1 = [ 
      'posAndWidth', 
      '="-1:8 =\'"', 
      'textAlignment', 
      '="Right"' 
     ]; 
posAndWidth ="-1:8 ='" textAlignment ="Right" 

Я включил вывод Хранитель, чтобы вы могли видеть, как строки разделены.

+0

Если [^ \ 3] * не делает то, что я делаю ... тогда что именно он делает? Он должен соответствовать любому символу, кроме цитирования, с которого мы начали, поэтому он должен остановиться в первой закрывающей цитате. Это ошибка в двигателе регулярных выражений? – ttsiodras

+0

@ttsiodras Внутри класса персонажей я сомневаюсь, что метасимволы работают. В этом случае вы пытаетесь отрицать '\ 3', независимо от того, что превращается в. Вы пробовали использовать re 'debug''? – TLP

+3

Обсуждение отрицательной обратной ссылки: http://www.perlmonks.org/?node_id=747135 В ней объясняется, что '[^ ...]' не работает с обратной ссылкой в ​​ожидании TLP. – cooltea

Смежные вопросы