读取大文件并输出与多个参数匹配的部分

时间:2012-01-23 20:02:43

标签: perl awk

我很少需要处理脚本,所以我反对对此问题缺乏了解。

我有一个文件> 500mb的文字,这是很好的切片,但我知道里面有5到10个“坏”部分。部分中的数据可以很容易地被人类评估,我不知道如何在程序中进行评估。

我在#Field MyField中选择了一个已知的良好价值 - 但是如果该值未出现在#FIELD LOCATION中,则出现问题。

文件中两个部分的示例如下所示。第一个是“坏”,第二个是“好”。

#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
  1. 章节以逻辑方式开始和结束,#START#END

  2. 如果#FIELD LOCATION不存在,请转到下一部分

  3. 如果#FIELD MyField="BAR"#FIELD LOCATION不包含BAR,请将此部分中的所有行打印到新文件中。

  4. 注意 - 澄清#FIELD MyField="BAR" - 这是我通过在构建此文件时抓取有关数据的其他信息而放入的检查值(在我的情况下,它是一种语言指示符,例如EN或者DE。所以它实际上是#FIELD MyField="EN"这个字段中的任何其他值都将被忽略,这不是符合我标准的记录。

  5. 我相信这可以在Awk或Perl中完成,我可以做非常简单的单行,但这超出了我的技能。

4 个答案:

答案 0 :(得分:3)

您可以执行以下操作。这只是草稿,但它可以处理您的样本数据。使用flip-flop operator查找记录的开头和结尾。使用哈希值存储字段值,使用数组存储记录。

我只是检查值是否在位置字符串中,您可能希望通过确保它位于正确的位置或正确的情况来进一步缩小检查范围。

use strict;
use warnings;

my @record;
my %f;
while(<DATA>) {
    if (/^#START / .. /^#END */) {
        if (/^#START /) {
            @record = (); # reset
            %f = ();
        }
        push @record, $_;
        if (/^#END */) { # check and print
            if ($f{'LOCATION'} !~ /$f{'MyField'}/) {
                print @record; 
            }
        } else {         # add fields to hash
            if (/^#FIELD (.+)/) {
                            # use split with limit of 2 fields
                my ($key, $val) = split /=/, $1, 2;
                next unless $val; # no empty values
                $val =~ s/^"|"$//g; # strip quotes
                $f{$key} = $val;
            }
        }
    }
}

__DATA__
#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor
#FIELD LOCATION=http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

答案 1 :(得分:2)

一衬垫:

perl -ne 'BEGIN { $/ = "#END\n" }' -e '/MyField="(.*?)"/; print if !/Value=$1/' <file >newfile

Input Record Separator设置为"#END\n",以便perl一次将“块”读入$_,然后捕获MyField中的值并打印整个块{{1} (即,'Value ='之后的捕获)存在。

如果需要,您当然可以使正则表达式更具体。

答案 2 :(得分:2)

这里有一个小gawk单行 -

gawk '
{
    if ($2!~/^#FIELD LOCATION/)
    {
        next;
    }
    else
    {
        split($2,ary,"=|&");
        split($4,ary1,"=|\"");
        if(ary[4]!=ary1[3])
            {
                print $0 > "badrec.file"
            }
    }
}' RS="#END\n" ORS="#END\n" FS="\n" file

输入文件:

[jaypal:~/Temp] cat file
#START Descriptor # Good Record
#FIELD LOCATION="http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor # Bad Record
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor # Good Record
#FIELD LOCATION="http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

测试:

[jaypal:~/Temp] gawk '
{
    if ($2!~/^#FIELD LOCATION/)
    {
        next;
    }
    else
    {
        split($2,ary,"=|&");
        split($4,ary1,"=|\"");
        if(ary[4]!=ary1[3])
            {
                print $0 > "badrec.file"
            }
    }
}' RS="#END\n" ORS="#END\n" FS="\n" file

[jaypal:~/Temp] cat badrec.file 
#START Descriptor # Bad Record
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

答案 3 :(得分:0)

input record separator设置为#END\n并直接读取记录:

#!/usr/bin/perl

$/ = "#END\n";

while (<DATA>) {
    next unless /^#FIELD LOCATION/m;
    /^#FIELD MyField="(.*)"$/m;
    next if /^#FIELD LOCATION.*$1/m;
    print
}



__DATA__
#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor
#FIELD LOCATION=http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END