【问题标题】:Intelligent RegEx in Perl?Perl 中的智能正则表达式?
【发布时间】:2009-12-11 17:55:42
【问题描述】:

背景


考虑以下输入:

<Foo
    Bar="bar"
    Baz="1"
    Bax="bax"
>

处理后,我需要它如下所示:

<Foo
    Bar="bar"
    Baz="1"
    Bax="bax"
    CustomAttribute="TRUE"
>

实施


这是所有我需要为不超过 5 个文件做的事情,所以使用正则表达式以外的任何东西似乎都过大了。无论如何,我想出了以下(Perl)正则表达式来完成这个:

$data =~ s/(<\s*Foo)(.*?)>/$1$2 CustomAttribute="TRUE">/sig;

问题


这很好用,但是有一个明显的问题。这种模式是“愚蠢的”,因为如果 CustomAttribute 已经被添加,上面概述的操作只会盲目地追加另一个 CustomAttribute=...

当然,一个简单的解决方案是编写一个辅助表达式,在运行替换操作之前尝试匹配CustomAttribute

问题


由于我对脚本语言和正则表达式领域相当陌生,我想知道是否可以在不引入任何宿主语言结构(即 Perl 中的 if 语句)的情况下解决这个问题,并且只需使用比我上面写的更“智能”的版本?

【问题讨论】:

  • omg 另一个 HTML + 正则表达式注释,HTML 不是常规语言,请使用解析器
  • 如果只有五个文件,请手动更改。
  • 造成问题的不是您对语言的不熟悉,而是您不理解这种“愚蠢”的解决方案解决了您提出的唯一问题。当您提供一个简单的示例并且不解释转换背后的原理 时,您将获得“解决”简单示例但可能无法“解决”一般应用程序的解决方案。所以你真的希望你的所有标签都有一个CustomAttribute="TRUE" 属性吗?
  • @Ether:五个文件并不意味着他只有五个替换。也许每个文件都有 1,000,000 条记录 :)
  • 不要使用正则表达式,因为你认为你的问题很小。当它们是可以为您提供正确答案的工具时,请使用它们。

标签: html regex perl parsing


【解决方案1】:

我不会在你不应该为此使用正则表达式的问题上打败你。我的意思是,您不应该,但您显然从您在问题中所说的内容中知道这一点,所以继续……

可以完成您所要求的事情称为negative lookahead assertion(通常是(?!...)),它基本上表示如果在此之前找到断言中的模式,您不希望匹配应用观点。在您的示例中,如果 CustomAttribute 已经存在,您不希望它应用,所以:

$data =~ s/(<\s*Foo)(?![^>]*\bCustomAttribute=)(.*?)>/$1$2CustomAttribute="TRUE">/sig;

【讨论】:

  • 太棒了,感谢您实际回答了这个问题,而不是在讲 RegEx + XML。
  • XML 允许在属性值中使用纯 &gt;,因此 &lt;Foo Bar="&gt;"&gt; 会失败。 &lt;Foo Bar="CustomAttribute="&gt; 也会失败。
  • @Gumbo:是的!感谢您指出为什么 HTML 的正则表达式本质上是脆弱的。如果没有下降解析,一定会发现这些异常;可能是在脆弱的代码被期望它更健壮的用户使用之后的几个月或几年。
【解决方案2】:

这听起来像是XML::Twig 的工作,它可以处理 XML 并在遇到 XML 时更改其中的部分内容,包括向标签添加属性。我怀疑您会花费尽可能多的时间来习惯 Twig,并且您会找到一个仅在大多数情况下有效的正则表达式解决方案。而且,最后你会知道足够多的 Twig 在下一个项目中使用它。 :)

【讨论】:

    【解决方案3】:

    我想是时候讲课了;--)

    我不确定您为什么认为使用成熟的 XML 处理器是多余的。使用适当的工具编写代码实际上更容易。正则表达式将更加复杂,并且将依赖于对数据的不成文假设,这是危险的。其中一些假设可能是:属性值中没有“>”,没有 CDATA 部分,标签或属性名称中没有非 ascii 字符,一致的属性值引用......

    正则表达式唯一能给您的是保证输出保持数据的原始格式(在您的情况下,属性每个都在单独的行上)。但是如果你的格式是一致的,那是可以做到的,如果不是,那也没关系,除非你把你的 XML 保存在一个面向行的版本控制系统中。

    这是一个使用 XML::Twig 的示例。它假定您有足够的内存来将任何整个 Foo 元素保存在内存中,并且它甚至可以在 DATA 部分中公认的 XML 部分上工作。使用 XML::LibXML 可能同样容易(读取内存中的 XML,选择所有 Foo 元素,为每个元素添加属性,输出,据我计算,这是 5 行易于理解的行)。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use XML::Twig;
    
    my( $tag, $att, $val)= ( 'Foo', 'CustomAttribute', 'TRUE');
    
    XML::Twig->new(                 # only process those elements
                    twig_roots => { $tag => sub { 
                                                  # add/set attribute
                                                  $_->set_att( $att => $val); 
                                                  # output and free memory
                                                  $_->flush;
                                                }
                                  },
                    twig_print_outside_roots => 1, # output everything else
                    pretty_print => 'cvs',         # seems to be the right format
                  )
             ->parse( \*DATA)  # use parsefile( $file) if parsing... a file
             ->flush;          # not needed in XML::Twig 3.33
    __DATA__
    <doc>
      <Foo
          Bar="bar"
          Baz="1"
          Bax="bax"
      >
      here is some text
      </Foo>
      <Foo CustomAttribute="TRUE"><Foo no_att="1"/></Foo>
      <bar><![CDATA[<Foo no_att="1">tricked?</Foo>]]></bar>
      <Foo><![CDATA[<Foo no_att="1" CustomAttribute="TRUE">tricked?</Foo>]]></Foo>
      <Foo
          Bar=">"
          Baz="1"
          Bax="bax"
      ></Foo>
      <Foo
          Bar="
    >"
          Baz="1"
          Bax="bax"
      ></Foo>
      <Foo
          Bar=">"
          Baz="1"
          Bax="bax"
          CustomAttribute="TRUE"
      ></Foo>
      <Foo
          Bar="
    >"
          Baz="1"
          Bax="b
    ax"
          CustomAttribute="TR
    UE"
      ></Foo>
    </doc>
    

    【讨论】:

      【解决方案4】:

      您可以通过带有“e”修饰符的函数发送匹配项以进行更多处理。

      my $str = qq`
      <Foo
          Bar="bar"
          Baz="1"
          Bax="bax"
          CustomAttribute="TRUE"
      >
      <Foo
          Bar="bar"
          Baz="1"
          Bax="bax"
      >
      `;
      
      sub foo {
          my $guts = shift;
          $guts .= qq` CustomAttribute="TRUE"` if $guts !~ m/CustomAttribute/;
          return $guts;
      }
      $str =~ s/(<Foo )([^>]*)(>)/$1.foo($2).$3/xsge;
      

      【讨论】:

      • &lt;Foo Bar="CustomAttribute"&gt; 呢?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-22
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      相关资源
      最近更新 更多