如何替换字符串的中间?

时间:2010-09-01 15:49:47

标签: regex perl

$a = "<no> 3232 </no> "

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

我希望$a变为"<no> 000 </no> ",但它无效。

6 个答案:

答案 0 :(得分:10)

您需要look-around assertions

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

您是否考虑过阅读Perl一两本书?如果你必须通过阅读精美的文档来提出可以轻松回答的那类问题,那么你就无法有效学习。

答案 1 :(得分:5)

你可以放弃花哨的前瞻或外观断言,并提出一个稍微长一点的正则表达式:

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

它可能更容易阅读,但有点违反直觉,因为你用<no>whatever</no>替换<no>000</no>,即你不只是替换{{1}之间的东西你正在用另一个字符串替换整个字符串,恰好其中包含<no></no><no>

答案 2 :(得分:4)

如果您只想替换标签之间的文字,那么您可能需要查看lookahead and lookbehind assertions。你需要使用除“/”之外的正则表达式分隔符或者在正则表达式中转义“/”:

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

答案 3 :(得分:3)

首先,/ in被解释为模式的结束,这会导致语法错误。为替换运算符选择不同的分隔符:

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

但是你有一组捕获括号而你没有使用它们捕获的东西。这让我觉得,甚至修复语法也不会给你你想要的行为。您不想替换标签,因此您可以将其添加到替换标签中:

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

或者根本不使用外观来替换它们,因此它们不是匹配文本的一部分:

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

但鉴于“它不起作用”并不能很好地描述问题,我不知道你期待看到什么。

答案 4 :(得分:1)

首先,结束时的/被视为正则表达式的结束引用。要么反斜杠:

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

或在你的正则表达式中使用不同的字符:

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

其次,我猜你正试图用这个解析XML文档并更改数据。我还猜测你的文档中有多个 <no> ... </no>部分。您提供的正则表达式的问题是(.*)将尽可能匹配 ,即 first <no>和<之间的所有内容em> 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联机帮助页中进行了介绍,如果您要使用Perl正则表达式执行任何远程非常重要的操作,这是必读的。

$ perldoc perlre

希望所有的例子都能帮助澄清一些事情。

答案 5 :(得分:1)

为了保持尽可能简单,你有很多问题,所以我们首先要消除明显的问题。

首先,你不能在字符串中单独使用斜杠字符(“/”),因为它对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”)以便轻松更改而不必混淆正则表达式。的?正则表达式是为了确保正则表达式不会“贪婪,如果字符串中没有多个元素集合 - 这会导致。*在遇到匹配模式时立即进行匹配,在这种情况下“”。

相关问题