【问题标题】:Find and replace xml tags in html string in Perl with regex用正则表达式在 Perl 中的 html 字符串中查找和替换 xml 标签
【发布时间】:2014-05-26 09:23:35
【问题描述】:

我需要在不完整的 xml 的 html 字符串中查找和替换 xml 标签,这就是为什么我不能使用 xml 解析器来处理它。所以我需要手动找到xml标签,并用这些html字符串中的内容替换它们。

包含 xml 标签的 html 字符串示例:

some text<p>hello p</p>
<vars type="text" name="fname" age="64" style="<b>color='red'</b>
Class::SubClass->color" /> other text or html open tags like <p><table><tr>

所以我需要找到带有可变数量可选属性的xml“vars”标签,并用内容替换它们。

【问题讨论】:

    标签: html xml regex perl


    【解决方案1】:

    不要使用正则表达式来解析 HTML。而是使用实际的 HTML 解析器,例如 Mojo::DOMmojocast episode 5 有一个关于使用此模块的 8 分钟精彩视频。

    以下内容获取您的 html,并将您的特殊 vars 标签翻译成一些新文本。

    use strict;
    use warnings;
    
    use Mojo::DOM;
    
    # Parse
    my $dom = Mojo::DOM->new(do {local $/; <DATA>});
    
    for my $var ($dom->find('vars')->each) {
        my $type = $var->{type};
        my $name = $var->{name};
    
        $var->replace("<b>name is $name</b> + <i>type is $type</i>");
    }
    
    print $dom;
    
    __DATA__
    <html>
    <head>
    <title>Always use a parser, not a regex</title>
    </head>
    <body>
    some text<p>hello p</p>
    <vars type="text" name="fname" age="64" style="<b>color='red'</b>
    Class::SubClass->color" /> other text or html open tags like <p><table><tr><td></td></tr></table>
    
    </body></html>
    

    输出:

    <html>
    <head>
    <title>Always use a parser, not a regex</title>
    </head>
    <body>
    some text<p>hello p</p>
    <b>name is fname</b> + <i>type is text</i> other text or html open tags like <p></p><table><tr><td></td></tr></table>
    
    </body></html>
    

    【讨论】:

    • 你的解决方案看起来很不错,唯一的缺点是这个模块会加载整个框架 Mojo,我已经在我的应用程序中使用 Moose,所以为这么小的东西加载另一个框架会很重,是吗知道我可以使用任何其他独立的 Perl 模块来代替这个吗?我尝试使用正则表达式的原因是为了避免大量模块用于小任务。
    • 关于“不要使用正则表达式解析 HTML”,我查看了 Mojo::DOM::HTML 和 Mojo::DOM::CSS ,它们都使用解析输入的常规表达式。我还查看了 XML::TreePP,发现它还使用正则表达式来解析整个文档。所以我认为使用正则表达式就可以了。
    • 是的,当然使用正则表达式解析 html 是可行的。如果你足够了解这些模块的来源,你就会比大多数尝试过的人更有能力。总的来说,不重新创建轮子更明智,特别是因为这是一个比最初看起来更难做好的项目。不过,如果您有性能方面的问题,我可以理解您尝试的愿望。
    【解决方案2】:

    查看一些用于 XML 和 HTML 的 Perl 解析器,如上面 Miller 回答所指出的 Mojo::DOM 以及 XML::TreePP,我发现他们正在使用正则表达式来解析整个内容,所以我尝试了他们的正则表达式和得到了很好的结果,只是可能需要一些优化。

    这是我所做的:

    my $text =<<'XHTML';
    some text
    <p>hello p</p>
    <vars  type="text" name= "fname" single='single quoted' unqouted=noquotes hastags=" <b>color='red'</b> Class::SubClass->color"/>
    other text or html open tags like
    <vars type="text" name= "lname" single1='single quoted' unqouted1=noquotes hastags1=" <b>bgcolor='red'</b> Class::SubClass->bgcolor">
    <table><tr>
    <vars name="mname" />
    XHTML
    
    while ( $text =~ m{(<vars\s+([^\!\?\s<>](?:"[^"]*"|'[^']*'|[^"'<>\/])*)/?>)}sxgi ) {
        my $match = $1;
        my $args = $2;
        #print "[[$match]] \n{{$args}}\n\n";
    
        #parse name=value attributes, values may be double or single quoted or unquoted
        while ( $args =~ m/([^<>=\s\/]+|\/)(?:\s*=\s*(?:"([^"]*?)"|'([^']*?)'|([^>\s\/]*)))?\s*/sxgi ) {
            my $name = $1;
            #any better solution with regex above to just get $2
            my $value = $2? $2: ($3? $3 : $4);
            print "$name=$value\n";
        }
        print "\n";
    }
    

    这是预期的输出:

    type=text
    name=fname
    single=single quoted
    unqouted=noquotes
    hastags= <b>color='red'</b> Class::SubClass->color
    
    type=text
    name=lname
    single1=single quoted
    unqouted1=noquotes
    hastags1= <b>bgcolor='red'</b> Class::SubClass->bgcolor
    
    name=mname
    

    当然,代码中的变量 $match 具有整个匹配项,因此我可以将其替换为我的内容。

    匹配属性的第二个正则表达式需要优化,我对这一行不满意:

    my $value = $2? $2: ($3? $3 : $4);
    

    是否可以修改正则表达式以仅获取 $2 中的属性值。

    Mojo::Dom 中使用的正则表达式是

    my $ATTR_RE = qr/
      ([^<>=\s\/]+|\/)   # Key
      (?:
        \s*=\s*
        (?:
          "([^"]*?)"     # Quotation marks
        |
          '([^']*?)'     # Apostrophes
        |
          ([^>\s\/]*)    # Unquoted
        )
      )?
      \s*
    /x;
    my $END_RE   = qr!^\s*/\s*(.+)!;
    my $TOKEN_RE = qr/
      ([^<]+)?                                          # Text
      (?:
        <\?(.*?)\?>                                     # Processing Instruction
      |
        <!--(.*?)--\s*>                                 # Comment
      |
        <!\[CDATA\[(.*?)\]\]>                           # CDATA
      |
        <!DOCTYPE(
          \s+\w+
          (?:(?:\s+\w+)?(?:\s+(?:"[^"]*"|'[^']*'))+)?   # External ID
          (?:\s+\[.+?\])?                               # Int Subset
          \s*
        )>
      |
        <(
          \s*
          [^<>\s]+                                      # Tag
          \s*
          (?:$ATTR_RE)*                                 # Attributes
        )>
      |
        (<)                                             # Runaway "<"
      )??
    /xis;
    

    我只是弄乱了它来匹配是否带有或不带有斜杠 > 或 /> 的结束标记。

    【讨论】:

      猜你喜欢
      • 2014-11-26
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多