2010-09-01 6 views
-4
$a = "<no> 3232 </no> " 

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

ожидаем, что $a будет "<no> 000 </no> ", но не работает.Как заменить середину строки?

+5

Что вы подразумеваете под словом "it's not working"? –

+0

его замена всей строки на 000 – Tree

+12

Является ли это вопросом скрытности «Как изменить значение в XML-документе»? –

ответ

9

Вам нужно look-around assertions.

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

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

+1

Использование жадного соответствия ('. *' Вместо '. *?') Почти наверняка приведет к нежелательному поведению при наличии нескольких или вложенных тегов (вы, кажется, ожидали несколько тегов, так как вы указали флаг 'g'.) Даже использование ленивого соответствия ('. *?') приведет к нежелательному поведению при наличии вложенных тегов. По крайней мере, ограничьте ущерб: 's/ [^ <]*<\/no>//g' или' s/(? <=) [\ s \ d] * (? = <\/no>)/000/g' – vladr

3

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

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

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

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

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

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

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

+1

Это решение также удаляет теги '' и ''. – mob

+1

Конечно. Но это не решение. Это повторяет то, что было у оригинального плаката, но без ошибок синтаксиса. Тогда, возможно, мы можем начать обсуждать то, что он действительно требует :-) –

+1

Теперь davorg исправляет: :) Я думаю, что OP, вероятно, делает что-то намного сложнее и упрощает его для нас. –

4

Если вы хотите заменить текст между тегами, вы можете посмотреть на lookahead and lookbehind assertions. И вам необходимо либо использовать регулярное выражение разделителем, кроме «/» или избежать «/» в регулярное выражение:

$a = "<no> 3232 </no> "; 
$a =~ s#(?<=<no>).*?(?=</no>)# 000 #gi; 
print "$a\n"; 
5

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

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

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

1

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

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

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

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

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

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

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

Поскольку до сих пор заменяет <no> ... </no>, вы, вероятно, хотите, чтобы положить их обратно в:

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

В случае где ваш <no> вместо этого является регулярным выражением, вы не можете просто поместить его в свою строку подстановки. Вы можете использовать lookarounds, как предложено другими, или просто захватить его и положить его обратно в использовании $ 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, которая является обязательной для чтения, если вы собираетесь делать что-либо даже удаленно нетривиально с регулярными выражениями Perl.

$ perldoc perlre 

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

1

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

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

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

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

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

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

Имея это в виду, вы могли бы попытаться придумать somethign как:

$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; 

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

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