使用regex在Perl中的html字符串中查找和替换xml标记

时间:2014-05-25 19:18:43

标签: html xml regex perl

我需要找到并替换html字符串中的xml标签,这不是完整的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&#34; vars&#34;带有可变数量的可选属性的标签,并用内容替换它们。

2 个答案:

答案 0 :(得分:2)

不要使用正则表达式来解析HTML。而是使用像Mojo::DOM这样的实际HTML解析器。有一个关于在mojocast episode 5使用此模块的8分钟视频。

以下内容将使用您的html,并将您的特殊变量标记转换为一些新文本。

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>

答案 1 :(得分:0)

看看一些Perl解析器的XML和HTML,如Moler :: DOM,如上面的Miller回答并查看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;

如果关闭标签有或没有斜线&gt;我只是搞砸了它以匹配或/&gt;。