【问题标题】:How to extract lines between a starting and ending regular expression in Perl如何在 Perl 中提取开始和结束正则表达式之间的行
【发布时间】:2016-07-18 22:38:58
【问题描述】:

我想用 Perl 循环一个文件(或一个数组),当匹配一个正则表达式时开始处理元素,当遇到另一个正则表达式时停止处理。

一种方法是将变量用作标志(当满足起始正则表达式时=1,当满足结束正则表达式时=0)。

例如,下面的作品,但非常丑陋!

use strict;

my @file = (
    "<title>List of widgets</title>\n",
    "<widgets>\n",
    "   <button>widget001.xml</button>\n",
    "   <textArea>widget002.xml</textArea>\n",
    "   <menu>widget002.xml</menu>\n",
    "</widgets>\n",
    "<footer>\n",
    "   This is the footer\n",
    "</footer>\n",
);

my $in_list_widgets = 0;
for my $line (@file) {
    if ($line=~m%<widgets%) {
        $in_list_widgets = 1;
    } elsif ($line=~m%</widgets>%) {
        $in_list_widgets = 0;
    } else {
        if ($in_list_widgets == 1) {
            &process_line($line);
        } else {
            #Do nothing
        }
    }
}

sub process_line {
    my $line = shift;
    print $line;
}

有什么更优雅的方法可以做到这一点并且仍然得到相同的结果?

<button>widget001.xml</button>
<textArea>widget002.xml</textArea>
<menu>widget002.xml</menu>

谢谢

【问题讨论】:

标签: regex perl


【解决方案1】:

碰巧这是 XML - 看起来确实如此 - 我建议使用 XML 解析器。

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig -> parse ( \*DATA );
$twig -> set_pretty_print('indented');

$_ -> print for map { $twig -> findnodes("//$_",0) } qw ( button textArea menu );

__DATA__
<root>
  <title>List of widgets</title>
  <widgets>
    <button>widget001.xml</button>
    <textArea>widget002.xml</textArea>
    <menu>widget002.xml</menu>
  </widgets>
  <footer>
   This is the footer
</footer>
</root>

输出:

<button>widget001.xml</button>
<textArea>widget002.xml</textArea>
<menu>widget002.xml</menu>

或者为了清楚起见:

my $twig = XML::Twig -> new -> parsefile('your_file'); 
foreach my $widgets ( $twig -> root -> children('widgets') ) {
   foreach my $child ( $widgets -> children ) { 
      $child -> print;
      print "\n";
   }
}

【讨论】:

  • 不错的代码。 XML 解析器确实做得很好。实际上,我发布这个问题是为了记住和分享关于更通用案例(而不仅仅是 XML)的翻转流运算符。再次感谢您的宝贵时间。
  • 范围运算符很有用,但对于 XML 来说是个糟糕的选择,因为格式、嵌套节点和上下文。
  • 是的。除非您 200% 确定现在有嵌套节点(例如,&lt;widgets&gt; 中没有 &lt;widgets&gt;)。
  • 即便如此。 XML 的关键在于它是一种数据传输语言。该规范对允许或不允许做什么是严格的。生成 XML 的人将遵循该规范。根据该规范,这意味着如果处理 XML 的人不是,那么有一天它可能会因为上游数据以一种完全有效的方式发生变化而神秘地中断。这真是糟糕的设计。
【解决方案2】:

您可以像这样使用语法&lt;match_regex_1&gt; .. &lt;match_regex_2&gt;

use strict;

my @file = (
    "<title>List of widgets</title>\n",
    "<widgets>\n",
    "   <button>widget001.xml</button>\n",
    "   <textArea>widget002.xml</textArea>\n",
    "   <menu>widget002.xml</menu>\n",
    "</widgets>\n",
    "<footer>\n",
    "   This is the footer\n",
    "</footer>\n",
);

my $in_list_widgets = 0;
for my $line (@file) {
    if ($line=~m%<widgets% .. $line=~m%</widgets>%) {
        &process_line($line) if ($line!~m%<(widgets|/widgets>)%);
    } else {
        #Do nothing
    }
}

sub process_line {
    my $line = shift;
    print $line;
}

一些解释:

  • if ($line=~m%&lt;widgets% .. $line=~m%&lt;/widgets&gt;%):当第一个条件为真时开始执行后面的块,直到最后一个条件为真。
  • &amp;process_line($line) if ($line!~m%&lt;(widgets|/widgets&gt;)%);:如果没有 if ($line!~m%...&lt;widgets&gt;&lt;/widgets&gt; 行也会被处理

希望对您有所帮助。

【讨论】:

  • 它被称为触发器操作符(如果你想用谷歌搜索它或其他东西)。
猜你喜欢
  • 2014-10-19
  • 1970-01-01
  • 1970-01-01
  • 2019-03-06
  • 2014-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多