Регулярное выражение, заменяющее открывающую скобку

В рамках сценария синтаксического анализа я пытаюсь преобразовать такие строки:

<a href="http://www.web.com/%20Special%20event%202013%20%282%29.pdf">

в

<a href="http://www.web.com/%20Special%20event%202013%20(2).pdf">

Регулярное выражение для закрывающей скобки работает нормально

perl -i -pe "s~(href\=\/?[\"\']\.\.\/$i\-(?:(?!%29).)*)%29([^\"\']*[\"\'])~\1)\2~g" "$pageName".html

давая мне

    <a href="http://www.web.com/%20Special%20event%202013%20%282).pdf">

Проблема возникает с эквивалентным регулярным выражением для открывающей скобки:

perl -i -pe "s~(href\=\/?[\"\']\.\.\/$i\-(?:(?!%28).)*)%28([^\"\']*[\"\'])~\1(\2~g" "$pageName".html                                

просто возвращает две группы, между которыми ничего нет:

<a href="http://www.web.com/%20Special%20event%202013%202%29.pdf">

Экранирование ( в замене с обратной косой чертой (или двумя) не имеет никакого эффекта. Если я заключу его в некоторые другие символы (скажем, ~\1#(#\2~g ), скобка все равно исчезнет (что даст мне %20##2 %29).

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

perl -i -pe "s~(href\=\/?[\"\']\.\.\/$i\-(?:(?!%28).)*)%28([^\"\']*[\"\'])~\1(((((((\L\2~g" "$pageName".html

выходы

<a href="http://www.web.com/%20Special%20event%202013%20(2%29.pdf">

Может кто-нибудь, пожалуйста, понять это.


person monototo    schedule 08.05.2013    source источник


Ответы (3)


Шаблон, который у вас есть, вообще не соответствует строке, которую вы показываете. Это соответствует чему-то похожему

<a href=/"../$i-xxxxxxxxxxxxxxx%29xxxxxxxxxx">

с буквальными точками и тем, что содержит $i.

Кроме того, пара моментов о вашей замене:

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

  • Не используйте \1, \2 и т. д. в строке замены. Perl очень старается, чтобы это работало, но обычно в Perl эти последовательности означают вставку символов \x01 и \x02. Используйте $1 и $2.

Таким образом, ваше регулярное выражение может быть написано

s~(href=/?["']\.\./$i-(?:(?!%29).)*)%29([^"']*["'])~$1)$2~;

но он по-прежнему не "работает нормально" с введенной вами строкой, которая должна выглядеть примерно так

<a href=/"../$i-xxxxxxxxxxxxxxx%282%29xxxxxxxxxx">

опять же, содержащий все, что находится в $i. Я вообще не понимаю необязательную косую черту перед значением атрибута href: это недопустимый HTML.

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

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

s/(href="[^"]+)%28(\d+)%29(\.pdf")/$1($2)$3/;

который отлично работает с строкой, которую вы дали, и сразу заменяет как открывающие, так и закрывающие скобки.

person Borodin    schedule 08.05.2013
comment
Спасибо. Это все хорошие общие советы. Я пытался упростить проблему, прежде чем опубликовать ее здесь, но, очевидно, значительно промахнулся. Я вернулся и почистил все дальше, добавил больше отладки и, в конце концов, проследил проблему до другой мошеннической замены в моем скрипте. - person monototo; 09.05.2013
comment
Чтобы прояснить пару моментов: $i — это переменная, потому что она находится внутри сценария оболочки. Во-вторых, я знаю, что необязательная косая черта не является допустимым HTML, я просто пытаюсь приспособить все, что создал редактор WYSISYG. В конечном счете, критическая оценка, которую вы дали, заставила меня вернуться и переоценить ситуацию еще несколько раз, пока я не понял ее. Спасибо! - person monototo; 09.05.2013
comment
Я понял, что $i была переменной оболочки, но я не мог согласовать ваше регулярное выражение со строкой, которая, как вы утверждали, совпадала. Любая строка, которую я пробовал, которая соответствовала первому шаблону, также соответствовала бы второму, поэтому я не мог воспроизвести вашу проблему. Здесь есть веский аргумент в пользу написания всего этого в виде Perl-скрипта, а не зловещей смеси оболочки и Perl, где никто не уверен, сколько обратных слэшей должно быть в любом месте. Perl гораздо более универсален, чем любая оболочка, и вы должны сосредоточить свои усилия на его тщательном изучении. - person Borodin; 09.05.2013
comment
Да. Полностью согласен @Borodin. Масштаб проекта вырос из чего-то, что было разумным небольшим сценарием оболочки, в зверя, который нужно переписать. И, извините, ввод, который я вам дал, будет изменен дальше в сценарии, к тому времени, когда произойдет эта замена, он будет выглядеть примерно так: href=../$i-community_events/%20Special%20event_2013_%282%29.pdf - person monototo; 09.05.2013
comment
Справедливо. Но ты говоришь, что сейчас на правильном пути, и это самое главное. Мои услуги доступны для миграции, если вы заинтересованы. Спросите здесь, и я опубликую временный адрес электронной почты. В противном случае я желаю вам добра. - person Borodin; 09.05.2013

Возможно, следующее будет полезно или, по крайней мере, даст некоторое направление. Он будет работать на Perl версии 10 и выше.

use strict;
use warnings;
use v5.10.0; # For regex \K

use URI::Escape;

my $string = '<a href="http://www.web.com/%20Special%20event%202013%20%282%29.pdf">';
$string =~ s/.+2013%20\K([^.]+)(?=\.pdf)/uri_unescape($1)/e;
print $string;

Выход:

<a href="http://www.web.com/%20Special%20event%202013%20(2).pdf">

Оставил достаточно даты и пробела (%20) в качестве привязки, затем использовал \K для *K*eep всего этого. Затем перехватывается закодированный текст URI, который позже декодируется и используется в качестве текста подстановки.

person Kenosis    schedule 08.05.2013
comment
Спасибо за этот ответ, оказалось, что проблема была в другом месте моего сценария (другая замена заключалась в удалении скобок), но \K - удобный оператор для добавления в арсенал. - person monototo; 09.05.2013

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

 perl -pe "s~(href\s*=\s*\"[^\"]*)%28(.*?)%29~\$1(\$2)~g" input
person perreal    schedule 08.05.2013