【问题标题】:Regex matches but capture group remains uninitialized正则表达式匹配但捕获组仍未初始化
【发布时间】:2014-08-07 16:49:38
【问题描述】:

我是 perl 和正则表达式的新手。我想我理解这个想法以及如何使用正则表达式,但我在编写脚本时遇到了一个问题。我有一些页面的内容,我正在尝试阅读一些信息。

my @rows = split(/<tr(\s)bgcolor=.{8}/,$content);

foreach my $row(@rows){
    if( $row =~/<td\s+nowrap\s+align=.*\s?(bgcolor=.*\s+)?>\w*\s?<\/td>/ig){
    print $1;
    print $file_opt $row."\n";

    # there will be more code later on
    } 
}

这给了我一个$1 未初始化的错误。我知道当模式与字符串不匹配时会发生这种情况。但是我在 if 下有正则表达式 - 所以如果它进入 if,它确实匹配,对吗?如您所见,我将行打印到文件中。每个看起来像这样:

<td nowrap align="right">DOLNOŚLĄSKIE</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">4</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >1</td><td nowrap align="right">3</td><td nowrap align="right" bgcolor=#D0E0D0 >6</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >2</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >19</td><td nowrap align="right">0</td></tr>

来自$content 的所有不必要的东西都不在文件中。那么这个模式是否匹配?

【问题讨论】:

  • 改用解析器。
  • 不要为此使用正则表达式。请改用 HTML 解析器。
  • 不要使用正则表达式解析 HTML。使用适当的 HTML 解析模块。 您无法使用正则表达式可靠地解析 HTML,并且您将面临悲伤和挫败感。一旦 HTML 与您的期望发生变化,您的代码就会被破坏。有关如何使用已经编写、测试和调试的现有 Perl 模块的示例,请参阅 htmlparsing.com/perl
  • 感谢您的建议。我想我有示例代码的大学并不像每个男孩想象的那么好:D

标签: regex perl html-parsing


【解决方案1】:

不要手工制作正则表达式来解析 html,yadda yadda,现在回答您的实际问题:

“但是我在 if 下有正则表达式 - 所以如果它输入 if,它确实匹配,对吧?”

在您的正则表达式中,您的捕获组后面有一个 ? 量词。这意味着它可以(并且在您的示例中确实)与查找您的捕获组一次或一次不匹配。如果您的正则表达式的最佳匹配碰巧涉及捕获组零次,则不会捕获任何内容并且$1 保持为空。去掉那个问号,以确保您的正则表达式仅在它确实捕获某些内容时匹配。

如果在您的示例中像这样使用它,它可以工作并且确实捕获了一些东西。

虽然由于量词是贪婪的,人们可能会假设它总是会捕获一些东西(如此处所示,当它在没有量词的情况下突然工作时),那里有这么多量词,它只是另一个首先变得贪婪。

【讨论】:

    【解决方案2】:

    从您帖子中的代码来看,您似乎正在尝试为给定行中的每个表格单元格捕获bgcolor 属性。并非所有单元格都有bgcolor 集,但其中一些有。以下是使用 HTML::TreeBuilder 提取该信息的方法:

    use HTML::TreeBuilder 5 -weak;
    
    my $html = q{<td nowrap align="right">DOLNOŚLĄSKIE</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">4</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >1</td><td nowrap align="right">3</td><td nowrap align="right" bgcolor=#D0E0D0 >6</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >2</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >19</td><td nowrap align="right">0</td></tr>};
    
    my $t = HTML::TreeBuilder->new_from_content($html);
    
    foreach my $col ( $t->look_down('_tag','tr')->content_list ) {
      print $col->attr('bgcolor'), "\n" if defined $col->attr('bgcolor');
    }
    

    我确定您需要检索的不仅仅是这些,但鉴于您的问题的模糊描述和不完整的代码,我们只能确定这些。

    但重点是确凿的;不要用正则表达式解析 HTML,用 HTML 解析器解析 HTML。一开始的学习曲线稍微陡峭,但结果会更健壮、更易于维护,而且您学到的技能将适用于任何 HTML 文档,而不仅仅是这个特定的文档。

    HTML::TreeBuilder 附带了一些很好的文档,但您必须阅读其中的大部分内容才能理解整个内容。

    还有另一个 HTML 解析模块 Mojo::Dom,它与 Mojolicious 框架一起提供。就个人而言,我发现它更容易使用,但有时当我发布示例时,人们似乎会得出结论,他们必须加载一些重量级的 Web 框架才能使用它(这并不完全正确,但我厌倦了在上游游泳。;)。您可能想看看它,看看它是否更适合您的口味。这是一个例子:

    use Mojo::DOM;
    
    my $html = q{<td nowrap align="right">DOLNOŚLĄSKIE</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">0</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">4</td><td nowrap align="right" bgcolor=#D0E0D0 >0</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >1</td><td nowrap align="right">3</td><td nowrap align="right" bgcolor=#D0E0D0 >6</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >2</td><td nowrap align="right">1</td><td nowrap align="right" bgcolor=#D0E0D0 >19</td><td nowrap align="right">0</td></tr>};
    
    for my $td ( Mojo::DOM->new($html)->find('td[bgcolor]')->each ) {
      print $td->attr('bgcolor'), "\n";
    }
    

    这两个代码示例都会产生以下输出:

    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    #D0E0D0
    

    ...这可能不是非常有用,但正是您发布的代码似乎想要捕获的内容。至少这是一个起点,您应该能够适应自己的需求。

    我相信 Mojo::DOM 的文档更容易理解,这可能会有所作为,特别是如果您是 Perl 新手。我的建议是从那里开始,围绕该模块构建您的解决方案。从长远来看,你会比使用正则表达式从 HTML 中提取数据要好得多。

    Mojolicious 发行版在大多数系统上安装不到一分钟,并且包括 Mojo::DOM 模块,它本身非常轻量级。这是一个不错的选择。

    【讨论】:

    • 感谢您提供如此详尽的回答。我将明确地遵循您的建议并阅读有关您提到的软件包的更多信息:)
    • @kamila 要观看Mojo::DOM 的 8 分钟介绍视频,请查看Mojocast Episode 5
    猜你喜欢
    • 2016-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-16
    • 2021-12-24
    相关资源
    最近更新 更多