代码之家  ›  专栏  ›  技术社区  ›  mirezus

如何替换字符串的中间部分?

  •  -4
  • mirezus  · 技术社区  · 5 年前
    $a = "<no> 3232 </no> "
    
    $a =~ s/<no>(.*)</no>/000/gi ;
    

    我正等着呢 $a 变成 "<no> 000 </no> " 但它不起作用。

    6 回复  |  直到 14 年前
        1
  •  10
  •   daxim e.dan    14 年前

    你需要 look-around assertions .

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

    你考虑过读一两本Perl书吗?如果你不得不去栈溢出来问那些可以通过阅读好的文档轻松回答的问题,那么你就没有有效地学习。

        2
  •  5
  •   CanSpice    14 年前

    您可以放弃那些花哨的lookahead或lookaround断言,并提出一个稍长的正则表达式:

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

    它可能更容易阅读,但它有点违反直觉,因为你要替换 <no>whatever</no> 具有 <no>000</no> 也就是说,你不仅仅是在 <no></no> ,用另一个字符串替换整个字符串 <no> </no> 在里面。

        3
  •  4
  •   runrig    14 年前

    如果您只想替换标记之间的文本,那么您可能需要查看 lookahead and lookbehind assertions . 您需要使用除“/”之外的regex分隔符,或者在regex中转义“/”:

    $a = "<no> 3232 </no> ";
    $a =~ s#(?<=<no>).*?(?=</no>)# 000 #gi;
    print "$a\n";
    
        4
  •  3
  •   brian d foy    14 年前

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

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

    但是你有一组捕获括号,你没有使用它们捕获的内容。这让我觉得,也许即使修改语法也不能提供您想要的行为。您不想替换标记,因此可以将这些标记添加到替换中:

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

    或者根本不使用lookarounds替换它们,这样它们就不是匹配文本的一部分:

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

    但考虑到“它不起作用”并不是很好地描述这个问题,我不知道你希望看到什么。

        5
  •  1
  •   pndc    14 年前

    首先,结束语中的/被视为正则表达式的结束引号。或者反斜杠:

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

    或者在regex中使用不同的字符:

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

    其次,我猜您是在尝试用这个来解析XML文档并更改数据。我也猜你有 许多的 <no> </no> 文档中的节。您给出的正则表达式的问题是 (.*) 将匹配 尽可能多地 ,即 第一 <没有& 以及 最后的 </No & Gt; 在您的文档中, 包括 中间有其他标签。它 取代了 <没有& </No & Gt; .

    你可以使用一个非贪婪的匹配,这是一个将匹配 尽可能少 . 你可以在*后面加一个问号,这样:

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

    因为这仍然取代了 <没有& </No & Gt; ,您可能希望将这些放回:

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

    如果你的 <没有& 而是一个正则表达式,不能将其放入替换字符串中。您可以按照其他人的建议使用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正则表达式做一些非常重要的事情,那么必须阅读manpage。

    $ perldoc perlre
    

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

        6
  •  1
  •   Auctionitis    14 年前

    为了尽可能简单,您有许多问题,所以让我们先消除明显的问题。

    首先,不能使用斜线字符(“ / “)本身在字符串中,因为它对per具有特殊意义;例如” /n “表示打印新行,斜线也用于分隔regex的部分。当您希望使用斜杠作为文本时,解决方案是用反斜杠转义斜杠,告诉Perl您确实需要斜杠字符,而不是特殊字符。所以您的原始代码最好这样编写:

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

    现在Perl将解释 <\/no> 作为 </no>

    其次,你的正则表达式是错误的。s///regex指示Perl用第二节中的模式替换/重新格式化第一节中的模式。您的指令告诉Perl用“000”替换前两个斜杠之间的所有内容,并将其赋给变量$A。

    您在regex中使用的方括号允许您将表达式分解为smnaller片段并重新排列内容,但是您没有使用它们,但是您走的是正确的道路。若要重新使用要保留的第一组斜线中表达式的部分,请在它们周围放置括号。在表达式的第二部分中,您可以使用$1、$2等来引用这些“片段”,以引用每个括号集中的内容。

    记住这一点,你可能会想到一些类似的事情:

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

    这很接近(如上所述),但测试将显示它仍然不太正确;更令人迷惑的是,这次您将得到的输出是 </No & Gt; . 这是因为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”)很容易更改,而不必与regex混淆。这个?在regex中,这是为了确保regex不会“贪心,如果字符串中有一组以上的无元素-这会导致在遇到匹配模式时,.*到sstop匹配,在本例中是”。