【问题标题】:How to extract non-matching lines from two text files?如何从两个文本文件中提取不匹配的行?
【发布时间】:2012-11-09 09:35:23
【问题描述】:

我知道使用 perl 提取匹配行,但我想要两个文件中不匹配的行,即它们在两个文本文件中是唯一的文件。

文件1

one|E2027.1|073467|66   ATGCTATGTTTTGCTAAT  
one|E2002.1|073405|649  ATGAAAGCTTTAAAGAAA  
one|E2001.1|734704|201  ATGTTTTCAGGTATTATA  
one|E2025.1|073468|204  ATGAAACAGAAATATATT  
one|E2028.1|073431|578  ATGTTATTTAATTATGGT  
one|E2040.1|073743|862  ATGATTTATCCTAATAAT   

.........~2000 行这样的行

文件2

one|E2027.1|073467|66  
one|E5005.5|000005|005  
one|E2001.1|734704|201  
one|E2025.1|073468|204  
one|E2028.1|073431|578  
one|E2040.1|073743|862    

.........~2000 行这样的行

如何使用 perl 或 cmd 命令提取不匹配的行?
这里例如文件二的第 2 行是文件 2 独有的.....!!!

这是我目前所拥有的

foreach(@2) {
    @org=split('\t',$_);
    chomp($two=$_);
    foreach(@1) {
        if($_=~m/^$two.+/) {
            print OUT1 "$_";
        } else {
            print OUT2 "$_";
        }
    }
}

但其他输出会提供 GB 的数据。

【问题讨论】:

  • 如果您使用的是 Linux 发行版,请使用 diff
  • @squiguy 他可能在 Windows 上。他要求cmd 解决方案。

标签: perl cmd


【解决方案1】:

您必须先读入其中一个文件。然后您可以匹配另一个文件的每一行的内容。我使用来自List::Utilfirst 来做到这一点。 grep 也可以,但 first 在找到第一个匹配项后会停止,这样可以节省处理大文件的时间。

use strict;
use warnings;
use List::Util qw(first);
use 5.014;

my $file1 = <<"FILE1";
one|E2027.1|073467|66\tATGCTATGTTTTGCTAAT
one|E2002.1|073405|649\tATGAAAGCTTTAAAGAAA
one|E2001.1|734704|201\tATGTTTTCAGGTATTATA
one|E2025.1|073468|204\tATGAAACAGAAATATATT
one|E2028.1|073431|578\tATGTTATTTAATTATGGT
one|E2040.1|073743|862\tATGATTTATCCTAATAAT
FILE1

my $file2 = <<"FILE2";
one|E2027.1|073467|66
one|E5005.5|000005|005
one|E2001.1|734704|201
one|E2025.1|073468|204
one|E2028.1|073431|578
one|E2040.1|073743|862
FILE2

my @file1_content = map { (split(/\t/))[0] } split /\n/, $file1;

foreach my $line (split /\n/, $file2) {
  chomp $line; # we need that because the split above is just a filler
  next if first { $_ eq $line } @file1_content;
  say $line;
}

我强烈建议您在所有程序中使用strictwarnings。它们都可以帮助您发现小的、微妙的错误。以更具描述性的方式命名变量也是一个好主意。名为 @1@2 的数组非常糟糕。我很难理解哪个变量做了什么。

【讨论】:

    【解决方案2】:

    只是为了帮助您改进代码:

    foreach(@2) {
        @org=split('\t',$_);
        chomp($two=$_);
        foreach(@1) {
            if($_=~m/^$two.+/) {
                print OUT1 "$_";
            } else {
                print OUT2 "$_";
            }
        }
    }
    

    你知道内循环的代码多久执行一次吗? scalar(@2) * scalar(@1) 次,在您的示例中约为 400 万次。这就是为什么你的文件变得那么大的原因。将内部循环替换为

    $matched=0;
    foreach(@1) {
        if($_=~m/^$two.+/) {
            $matched=1;
            last;
        }
    }
    if($matched) {
        print OUT1 $_;
    } else {
        print OUT2 $_;
    }
    

    内部循环现在跟踪匹配并且写入文件只发生在外部循环中。 请注意,我已尝试适应您的编码风格!

    编码风格!啊! :D

    这种编码风格是从上个千年开始的!让我添加一些说明如何使您的代码更安全、更易读和更可调试:

    • 总是use strict;use warnings;。许多错误可以通过这种方式及早发现。
    • 不要使用全局(包)变量,这对strictures 来说并没有那么诱人。使用词法变量 (my @lines = ...)。
    • 使用正确的变量名:@1 不是很有帮助。事实上,使用它的单个元素 ($1[42]) 看起来很混乱,因为 $1 是 Perl 的正则表达式捕获变量。它不必非常富有诗意。一个简单的@lines 也可以,但即使@gargravarr 也比@1 好。
    • 当你不需要时不要使用字符串插值。可接受的用途:"Hi $name, what's up?"。坏:print "$_"。只需使用print $_
    • 使用空白。 if($_=~m/^$two.+/) 看起来像线路噪音。为了进行比较,请查看这段手工制作的精美 Perl 代码:
    foreach 我的 $line (@lines) {
        打印$差异$行
            if $line =~ /^$prefix.*/;
    }

    所以让我们尝试重写该代码:

    my $matched = 0;
    
    foreach my $line (@lines) {
        if ($line = ~/^$two.+/) {
            $matched=1;
            last;
        }
    }
    
    if ($matched) {
        print OUT1 $_;
    } else {
        print OUT2 $_;
    }
    

    现在感觉好多了! :) 知道你在做什么!不要只是复制'n'粘贴代码 sn-ps。

    【讨论】:

    • 你让我笑了@gargravarr:D
    【解决方案3】:
    #!/usr/bin/perl
    use strict;
    use warnings;
    
    open my $fh1 ,'<', 'f1' or die $!;
    open my $fh2 ,'<', 'f2' or die $!;
    chomp(my @ar1=<$fh1>);
    chomp(my @ar2=<$fh2>);
    close $fh1;
    close $fh2;
    
    my @ar3=();
    foreach my $x (@ar2) {
       push @ar3, $x if not grep (/^\Q$x\E/,@ar1);
    }
    print "@ar3";
    

    其中 f1 和 f2 是您的文件。

    【讨论】:

    • 如果您使用eq 而不是正则表达式,这会快很多。来自 List::Util 的first 也会使其更快(请参阅我的回答)。
    【解决方案4】:

    我知道了;前提是要比较的数据应该在两个文件中的单列中

    use strict;use warnings;
    print "Enter file1: ";
    chomp($file=<STDIN>);
    open(FH,$file);
    
    print"Enter file2: ";
    $hspfile=<STDIN>;
    open(FH1,$hspfile);
    
    my $list1;
    my $list2;
    my @list1 =<FH1> ;my @list2 =<FH> ;
    print "enter output file1 : ";
    $out = <STDIN>;
    chomp($out);
    open(OUT,">$out");
    LIST2: foreach $list2 (@list2){
    LIST1: foreach $list1 (@list1){
    if ("$list2" eq "$list1") {
    next LIST2;
    }
    }
    print OUT"$list2";
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-15
      • 1970-01-01
      • 2021-10-13
      • 2019-12-21
      • 2019-12-21
      • 2015-04-12
      • 2014-09-11
      相关资源
      最近更新 更多