Perl - 使用多个正则表达式从文件中删除多行

时间:2015-08-29 13:18:43

标签: regex perl

我(显然)是Perl的新手,我正在尝试创建一个简单的脚本,每周清理大约450万条记录中的大文件。我想完全删除匹配三种模式之一的行。该文件如下所示:

D0832
G2565
ZDS97
FHM2547
JDH1464
R2918
4918K
AG01023
AG02997

我的下面的脚本有效,但我得到一个删除发生(替换)的空白行,而不是完全删除该行。

#!/usr/bin/perl

open( FH, "serial.txt" ) || die "Couldn't open file...\n";

while ( <FH> ) {
   $data .= $_;
}

$data =~ s/[A][F|G][(0-9)]{5}//g;
$data =~ s/[A-Z][0-9][0-9][0-9][0-9]//g;
$data =~ s/[0-9][0-9][0-9][0-9][A-Z]//g;

print $data;
close( FH );

我的问题是 - 有450万条记录,每周至少运行一次,这是一种有效/快速的方法来完成我想做的事情,还是有更有效的方法来做到这一点?另外,如何删除行而不是替换空行?

谢谢大家。斯蒂芬

4 个答案:

答案 0 :(得分:3)

@ndn的评论是正确的。但是,就个人而言,我不是在整个文件中阅读,而是逐行处理它(我也冒昧地整理你的正则表达式):

#!/usr/bin/perl -p
$_ = '' if /^A[FG]\d{5}$/ || /^[A-Z]\d{4}$/ || /^\d{4}[A-Z]$/;

#!/usr/bin/perl -n
print unless /^A[FG]\d{5}$/ || /^[A-Z]\d{4}$/ || /^\d{4}[A-Z]$/;

(在这两种情况下,请在命令行中指定输入文件。阅读perlrun手册页,了解-p-n选项的工作方式。)

答案 1 :(得分:3)

首先,我会列出预编译模式,以测试每一行。问题可能会改变,我想添加和删除模式而不会打扰代码:

my @patterns = ( 
    qr/\A [A] [FG]  [0-9]{5} \Z/x,
    qr/\A [A-Z]     [0-9]{4} \Z/x,
    qr/\A [0-9]{4}  [A-Z]    \Z/x,
    );

while( my $line = <DATA> ) {
    next if grep { $line =~ $_ } @patterns;

    print $line;
    }

__END__
D0832
G2565
ZDS97
FHM2547
JDH1464
R2918
4918K
AG01023
AG02997

尽管如此,重大改进不是模式。它一次检查一行并打印我想要保留的行。我没有同时在内存中的整个文件;它一次只是一条线。

虽然这有问题。它有效,但每次都会检查每个模式。如果只有很少的线匹配或者只有少数模式,这可能并不意味着什么。如果您认为它可能很重要,那么使用List::Util而非first中的grep可以提供帮助,因为它只需找到一个匹配项并在找到时停止:

use List::Util qw(first);

my @patterns = ( 
    qr/\A [A] [FG]  [0-9]{5} \Z/x,
    qr/\A [A-Z]     [0-9]{4} \Z/x,
    qr/\A [0-9]{4}  [A-Z]    \Z/x,
    );

while( my $line = <DATA> ) {
    next if first { $line =~ $_ } @patterns;

    print $line;
    }

__END__
D0832
G2565
ZDS97
FHM2547
JDH1464
R2918
4918K
AG01023
AG02997

或者,我可能会做出一个巨大的模式。 Regexp::Assemble可以将它们放在一起(但如果你注意交替优先权,你也可以这样做):

use v5.10;

use Regexp::Assemble;

my @patterns = ( 
    '[A][FG][0-9]{5}',
    '[A-Z][0-9]{4}',
    '[0-9]{4}[A-Z]',
    );

my $grand_pattern = do {
    my $ra = Regexp::Assemble->new;
    $ra->add( $_ ) for @patterns;
    my $re = $ra->re;
    qr/ \A (?: $re ) \Z /x;
    };

say "Grand regex is $grand_pattern";

while( my $line = <DATA> ) {
    next if $line =~ $grand_pattern;

    print $line;
    }

__END__
D0832
G2565
ZDS97
FHM2547
JDH1464
R2918
4918K
AG01023
AG02997

下一步是从命令行或配置文件中获取模式,但这并不是那么难。该计划根本不应该知道模式。如果您不必更改代码,则可以更轻松地更改模式。

答案 2 :(得分:0)

不需要多个正则表达式模式。这将满足您的需求

perl -ne'print unless /^(?:[A][FG]\d{5}|[A-Z]\d{4}|\d{4}[A-Z])$/' serial.txt

输出

ZDS97
FHM2547
JDH1464

答案 3 :(得分:0)

 $data =~ s/[A-Z][0-9][0-9][0-9][0-9][\s\r\n]*//g;
 $data =~ s/[0-9][0-9][0-9][0-9][A-Z][\s\r\n]*//g;

从问题:

  

“如何删除行而不是替换空行?”

每个正则表达式的结尾,我们可以有换行/返回。然后正则表达式将取代空行。因此我添加了[\ s \ r \ n] *语法,它不会替换空行。

相关问题