Как заменить середину строки? - PullRequest
0 голосов
/ 01 сентября 2010
$a = "<no> 3232 </no> "

$a =~ s/<no>(.*)</no>/000/gi ;

Я ожидаю, что $a станет "<no> 000 </no> ", но это не работает.

Ответы [ 6 ]

10 голосов
/ 01 сентября 2010

Вам нужны проверочные утверждения .

$a =~ s|(?<=<no> ).*(?= </no>)|000|gi;
# $a is now "<no> 000 </no> "

Рассматривали ли вы чтение книги по Perl или двух?Вы не учитесь эффективно, если вам приходится приходить в Stack Overflow, чтобы задавать такие вопросы, на которые легко ответить, прочитав прекрасную документацию.

5 голосов
/ 01 сентября 2010

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

$str =~ s|<no>.*?</no>|<no>000</no>|gi;

Это может быть немного легче для чтения, но это немного нелогично, потому что вы заменяете <no>whatever</no> на <no>000</no>, то есть вы не просто заменяете вещи между <no></no>, вы замена всей строки другой строкой, в которой, как оказалось, содержатся <no> и </no>.

4 голосов
/ 01 сентября 2010

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

$a = "<no> 3232 </no> ";
$a =~ s#(?<=<no>).*?(?=</no>)# 000 #gi;
print "$a\n";
3 голосов
/ 01 сентября 2010

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

s|<no>.*</no>|000|gi;

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

s|<no>.*</no>|<no>000</no>|gi;

Или не заменять их вообще, используя обходные пути, чтобы они не были частью сопоставленного текста:

s|(?<=<no>).*(?=</no>)|000|gi;

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

1 голос
/ 14 сентября 2010

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

Во-первых, вы не можете использовать символ косой черты ("/") в строке, поскольку он имеет особое значение для per; например, «/n» означает печать новой строки, а косая черта также используется для разделения части регулярного выражения. Когда вы хотите использовать косую черту в качестве литерала, решение состоит в том, чтобы избежать косой черты с обратной косой чертой, чтобы сказать Perl, что вы действительно хотите использовать косую черту, а не что-то особенное. Таким образом, ваш оригинальный код будет лучше написан так:

$a = "<no> 3232 <\/no> ";
$a =~ s/<no>(.*)<\/no>/000/gi;

Теперь Perl будет интерпретировать <\/no> как </no>

Во-вторых, ваше регулярное выражение неверно. S /// regex указывает perl заменить / переформатировать шаблон в первом разделе на шаблон во втором разделе. Ваша инструкция в том виде, в каком она есть, говорит Perl, что нужно заменить все между первыми двумя слешами на «000» и присвоить ее переменной $ a.

Скобки, которые вы использовали в регулярном выражении, позволяют вам разбить выражение на куски smnaller и перестроить вещи, но вы их не использовали, однако вы на правильном пути. Чтобы повторно использовать части выражения в первом наборе слешей, которые вы хотите сохранить, вы заключаете их в квадратные скобки. Во второй части выражения вы можете ссылаться на эти «кусочки», используя $ 1, $ 2 и т. Д., Чтобы ссылаться на вещи в каждом наборе скобок.

Имея это в виду, у вас может возникнуть соблазн придумать что-нибудь вроде:

$a = "<no> 3232 <\/no> ";
$a =~ s/(<no>).*(<\/no>)/$1000$2/gi;

Это близко - как предложено выше - но тестирование покажет, что оно все еще не совсем верно; на этот раз вы получите еще более загадочный вывод </no>. Это связано с тем, что perl интерпретирует строку как $ 1000, за которыми следуют $ 2, а $ 1000 ни к чему не относится. Если после $ 1 поставить пробел или еще что-то, это решит проблему. (Вероятно, есть какой-то способ более корректного завершения $ 1, но я признаюсь, что я этого не знаю.)

Следующее выражение будет работать, но вы получите пробел после первого, поэтому ваш выход будет <no> 000</no>

$a = "<no> 3232 <\/no> ";
$a =~ s/(<no>).*(<\/no>)/$1 000$2/gi;

Я бы предпочел использовать переменную вместо строки "000", и по этой причине мой код мог бы выглядеть примерно так:

$a = "<no> 3232 <\/no> ";
$b = "000";
$a =~ s/(<no>).*?(<\/no>)/$1$b$2/gi;

Использование переменной, на мой взгляд, делает вещи более понятными (хотя их можно было бы назвать лучше!), А также позволяет легко заменять текст («000») без необходимости связываться с регулярным выражением. ? в регулярном выражении подразумевает, что регулярное выражение не становится «жадным», если в строке более одного набора элементов - это приводит к тому, что. * останавливает сопоставление, как только встречает соответствующий шаблон, в этом случае "".

1 голос
/ 14 сентября 2010

Во-первых, / в заключении обрабатывается как конечная кавычка для регулярного выражения.Или выполните обратную косую черту:

$a =~ s/<no>(.*)<\/no>/000/gi;

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

$a =~ s~<no>(.*)</no>~000~gi;

Во-вторых, я предполагаю, что вы пытаетесь проанализировать документ XML с этими изменить данные.Я также предполагаю, что у вас есть много <no> ... </no> разделов в вашем документе.Проблема с заданным вами регулярным выражением состоит в том, что (.*) будет соответствовать в максимально возможной степени , то есть всему, что находится между first <no> и last </no> в вашем документе, включая любые другие теги между ними.Это также заменяет <no> и </no>.

Вы можете использовать не жадное совпадение, то есть такое, которое будет соответствовать как можно меньше .Вы можете поставить вопросительный знак после *, например, так:

$a =~ s~<no>(.*?)</no>~000~gi;

Так как он по-прежнему заменяет <no> ... </no>, вы, вероятно, захотите поставить его обратно:

$a =~ s~<no>(.*?)</no>~<no>000</no>~gi;

В случае, когда ваш <no> является регулярным выражением, вы не можете просто поместить его в строку замещения.Вы можете использовать обходные пути, предложенные другими, или просто захватить их и вернуть обратно, используя $ 1 .. $ 9, например:

$a =~ s~(<no>)(.*?)(</no>)~$1000$3~gi;

Почему 3 $?Потому что 2 доллара - это то, что вы поймали с (.*?).Конечно, так как вы на самом деле не заботитесь о том, что вы захватили, вы можете просто сделать это:

$a =~ s~(<no>).*?(</no>)~$1000$2~gi;

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

Кроме того, обычно плохая идея пытаться анализировать XML с помощью регулярных выражений, потому что XML слишком разнообразен для анализа регулярными выражениями.Мне очень нравится XML::LibXML для обработки документов XML, но это совсем не так просто.Однако, если вы уверены в точном формате вашего XML (или на самом деле это не XML, а просто выглядит немного похожим на него), то регулярные выражения в порядке локального хака.

Все это рассматривается вperlre man-страница, которую необходимо прочитать, если вы собираетесь делать что-то даже нетривиально с регулярными выражениями Perl.

$ perldoc perlre

Надеюсь, что все примеры помогут прояснить ситуацию немного.

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