我(显然)是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万条记录,每周至少运行一次,这是一种有效/快速的方法来完成我想做的事情,还是有更有效的方法来做到这一点?另外,如何删除行而不是替换空行?
谢谢大家。斯蒂芬
答案 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] *语法,它不会替换空行。