如果特定单词在给定的数据子集中只出现一次,如何替换它?

时间:2013-10-30 01:55:12

标签: perl

考虑下面的数据集。每个以数字开头的块都是“案例”。在真实的数据集中,我有成千上万的案例。如果案例中只有一个词排除(例如案例10001),我想将“排除”一词改为“0”。

如果我遍历行,我可以计算每种情况下有多少“排除”。但是,如果只有一行包含“排除”一词,我不知道如何回到该行并替换该词。

我该怎么做?

10001
M1|F1|SP1;12;12;12;11;13;10;Exclusion;D16S539
M1|F1|SP1;12;10;12;9;11;9;3.60;D16S
M1|F1|SP1;12;10;10;7;11;7;20.00;D7S
M1|F1|SP1;13;12;12;12;12;12;3.91;D13S
M1|F1|SP1;11;11;13;11;13;11;3.27;D5S
M1|F1|SP1;14;12;14;10;12;10;1.99;CSF
10002
M1|F1|SP1;8;13;13;8;8;12;2.91;D16S
M1|F1|SP1;13;11;13;10;10;10;4.13;D7S
M1|F1|SP1;12;9;12;10;11;16;Exclusion;D13S
M1|F1|SP1;12;10;12;10;14;15;Exclusion;D5S
M1|F1|SP1;13;10;10;10;17;18;Exclusion;CSF

4 个答案:

答案 0 :(得分:4)

sub process_block {
   my ($block) = @_;
   $block =~ s/\bExclusion\b/0/
      if $block !~ /\bExclusion\b.*\bExclusion\b/s;
   print($block);
}

my $buf;
while (<>) {
    if (/^\d/) {
        process_block($buf) if $buf;
        $buf = '';
    }

    $buf .= $_;
}

process_block($buf) if $buf;

答案 1 :(得分:2)

在阅读文件时,缓冲案例中的所有行,并计算排除项

my ($case,$buf,$count) = (undef,"",0);
while(my $ln = <>) {

使用正则表达式来检测案例,

    if( $ln =~ /^\d+$/ ) {
        #new case, process/print old case
        $buf =~ s/;Exclusion;/;0;/ if($count==1);
        print $buf;
        ($case,$buf,$count) = ($ln,"",0);
    }

现在使用正则表达式检测“排除”?

    elsif( $ln =~ /;Exclusion;/ ) { $count++; }
    $buf .= $l;
}

当你完成后,你可能还有一个案件需要处理,

if( length($buf)>0 ) {
    $buf =~ s/;Exclusion;/;0;/ if($count==1);
    print $buffer;
}

答案 2 :(得分:1)

这是我能想到的最好的。假设您将文件读入@lines

# separate into blocks                                                                 
foreach my $line (@lines) {
    chomp($line);
    if ($line =~ m/^(\d+)/) {
        $key = $1;
    }
    else {
        push (@{$block{$key}}, $line);
    }
}

# go through each block                                                                
foreach my $key (keys %block) {
    print "$key\n";
    my @matched = grep ($_ =~ m/exclusion/i, @{$block{$key}});
    if (scalar (1 == @matched)){
        foreach my $line (@{$block{$key}}) {
            $line =~ s/Exclusion/0/i;
            print "$line\n";
        }
    }
    else {
        foreach my $line (@{$block{$key}}) {
            print "$line\n";
        }
    }
}

答案 3 :(得分:1)

这里已经有很多正确答案,它们使用缓冲区来存储“案例”的内容。

这是使用tellseek来回放文件的另一种解决方案,因此不需要缓冲区。当您的“案例”非常大并且您对性能或内存使用敏感时,这可能很有用。

use strict;
use warnings;

open FILE, "text.txt";
open REPLACE, ">replace.txt";

my $count = 0;      # count of 'Exclusion' in the current case
my $position = 0;
my $prev_position = 0;
my $first_occur_position = 0;   # first occurence of 'Exclusion' in the current case
my $visited = 0;    # whether the current line is visited before

while (<FILE>) {
    # keep track of the position before reading
    # the current line
    $prev_position = $position;
    $position = tell FILE;

    if ($visited == 0) {
        if (/^\d+/) {
            # new case
            if ($count == 1) {
                # rewind to the first occurence 
                # of 'Exclusion' in the previous case
                seek FILE, $first_occur_position, 0; 
                $visited = 1;
            }
            else {
                print REPLACE $_;
            }
        }
        elsif (/Exclusion/) {
            $count++;
            if ($count > 1) {
                seek FILE, $first_occur_position, 0;
                $visited = 1;
            }
            elsif ($count == 1) {
                $first_occur_position = $prev_position;
            }
        }
        else {
            print REPLACE $_ if ($count == 0);
        }

        if (eof FILE && $count == 1) {
            seek FILE, $first_occur_position, 0;
            $visited = 1;
        }
    }
    else {
        if ($count == 1) {
            s/Exclusion/0/;
        }
        if (/^\d+/) {
            $position = tell FILE;
            $visited = 0;
            $count = 0;
        }
        print REPLACE $_;
    }
}

close REPLACE;
close FILE;