如何在Perl中阅读时跳过一些块内容

时间:2010-06-03 00:56:14

标签: perl expression

我打算跳过包含“MaterialiseU4()”起始行的块内容,下面是subroutin()read_block。但失败了。

# Read a constant definition block from a file handle. 
# void return when there is no data left in the file. 
# Otherwise return an array ref containing lines to in the block.  
sub read_block { 
    my $fh = shift; 

    my @lines; 
    my $block_started = 0; 

    while( my $line = <$fh> ) { 

    # how to correct my code below? I don't need the 2nd block content.
 $block_started++ if ( ($line =~ /^(status)/) && (index($line, "MaterializeU4") != 0) ) ;

 if( $block_started ) { 

     last if $line =~ /^\s*$/; 

     push @lines, $line; 
 }

    } 
    return \@lines if @lines;
    return; 
} 

数据如下:

__DATA__ 
status DynTest = <dynamic 100>
vid = 10002
name = "DynTest"
units   = ""

status VIDNAME9000 = <U4 MaterializeU4()>
vid = 9000
name = "VIDNAME9000"
units = "degC"

status DynTest = <U1 100>
vid = 100
name = "Hello"
units   = ""

输出:

  <StatusVariables>
    <SVID logicalName="DynTest" type="L" value="100" vid="10002" name="DynTest" units=""></SVID>
    <SVID logicalName="DynTest" type="L" value="100" vid="100" name="Hello" units=""></SVID>
  </StatusVariables>

[更新] 我打印index($line, "MaterializeU4")的值,输出25。 然后我更新了以下代码

$block_started++ if ( ($line =~ /^(status)/) && (index($line, "MaterializeU4") != 25)

现在它有效。

欢迎任何评论我的做法。

3 个答案:

答案 0 :(得分:6)

Perl已经有一个操作员来跟踪块。它被称为“触发器”操作符:

试试这个:

while ( <DATA> ) { 
   next if /\Q<U4 MaterializeU4()>\E/../^\s*$/;
   push @lines, $_;
}

/\Q<U4 MaterializeU4()>\E/../^\s*$/的值将为真它看到与起始正则表达式匹配的行时,它会在看到与第二个表达式匹配的行后停止为真。

答案 1 :(得分:1)

在成功匹配子字符串时,index返回子字符串的位置,该字符串可以是任何值&gt; = 0.在“失败”时,index返回-1。

您使用index

的方式
index($line, "MaterializeU4") != 0
除了以字符串"MaterializeU4"开头的行之外,

对于所有行都是true。

看起来你已经对Perl正则表达式有了一些了解。为什么不在这种情况下使用一个呢?

++$block_started if $line =~ /status/ && $line =~ /MaterializeU4/;

我看到的另一个问题是你设置$block_started来开始捕获行,但是你不会在“块”结尾处将它设置为零,比如,当$line为空时。我不确定这是不是你想做的事。

答案 2 :(得分:1)

首先,使用正则表达式而不是索引可能更好,因为如果您可能决定更严格而不仅仅是“子串存在”,则可以将其调整为状态字符串的确切格式

我建议作为一个解决方案添加第二个标志来跳过块内容,如果它是MaterializeU4块,如下所示:

# Read a constant definition block from a file handle. 
# void return when there is no data left in the file. 
# Empty return for skippable (Materialize4U) block!!!
# Otherwise return an array ref containing lines to in the block.  
sub read_block { 
    my $fh = shift; 
    my @lines = (); 
    my $block_started = 0; 
    my $block_ignore = 0;
    while (my $line = <$fh> ) { 
        if ($line =~ /^status.*?((MaterializeU4)?)/) {
            $block_started = 1;
            $block_ignore = 1 if $1;
        }
        last if $line =~ /^\s*$/ && $block_started;
        push @lines, $line unless $block_ignore; 
    } 
    return \@lines if @lines || $block_started;
    return; 
} 

这是我使用codepad.org测试的略微修改的样本:

<强>代码:

use Data::Dumper;
my @all_lines = (
  "s 1" ,"b 1" ,""
, "s MaterializeU4" ,"b 2" ,""
, "s 3" ,"b 3" ,""
);

while (@all_lines) {
    my $block = read_block();
    print Data::Dumper->Dump([$block]);
}
exit 0;

sub read_block { 
    my @lines = (); 
    my $block_started = 0; 
    my $block_ignore = 0;
    while (my $line = shift @all_lines) { 
        if ($line =~ /^s .*?((MaterializeU4)?)/) {
            $block_started = 1;
            $block_ignore = 1 if $1;
        }
        last if $line =~ /^\s*$/ && $block_started;
        push @lines, $line unless $block_ignore; 
    } 
    return \@lines if @lines || $block_started;
    return; 
} 

<强>输出

$VAR1 = [
          's 1',
          'b 1'
        ];
$VAR1 = [];
$VAR1 = [
          's 3',
          'b 3'
        ];