如何用Perl分割长文件并打印输出到不同的文本文件?

时间:2019-02-12 06:32:22

标签: perl text hash split

我有一个3列的长文本文件。

输入文件:

XIGO    XIGO_24480  Xigou  
XIGO    XIGO_24481  Xigou  
XOLO    XOLO_Z1E01  Xoloitzcuintle  
XOLO    XOLO_Z1G01  Xoloitzcuintle  
YORK    TYo_0GT393  Yorkshire Terrier  
YORK    TYo_0GT394  Yorkshire Terrier  

我想要带有数字标签的输出文本文件。该列表按第三列拆分。

File_1.txt

XIGO    XIGO_24480   
XIGO    XIGO_24481  

File_2.txt

XOLO    XOLO_Z1E01   
XOLO    XOLO_Z1G01  

File_3.txt

YORK    TYo_0GT393   
YORK    TYo_0GT394  

我尝试使用哈希函数在Perl中拆分文件。但是,我仍然找不到正确的文件。

#!/usr/bin/perl -w
use strict;
use warnings;

my $input = 'File_List_1.txt';
my %results;
my $out;
my $FID;
my $IID;
my $Breed;
my $results;
my @array;
my $index=0;

open(my $fh, '<', $input) or die "cannot open input file: $!";


 while (<$fh>) {
   chomp;
   my ($FID, $IID, $Breed) = split '\t', $_;
   $results{$Breed}{$IID} = $FID;
 }

 for my $values (keys %results) {
 open (my $out, '>', 'File_',"$index.txt") or die "cannot open input file: $!";

 for my $values_1 (keys %{$results{$values}}){
   print $out, join ("\t" , map {$results{$values}->{$values_1},$values_1} keys%results);
 }
 close $out;

有什么建议吗?谢谢

2 个答案:

答案 0 :(得分:2)

我认为这就是您想要的:

  • 从STDIN读取输入行
    • 分为第一部分+键
    • 如果我们以前没看过钥匙
      • 使用文件名中的索引打开新文件进行写入
      • 使用密钥将文件句柄存储在哈希中。
      • 将索引增加一个
    • 使用键从哈希中获取文件句柄并将其写入第一部分
  • 完成后关闭所有打开的文件句柄
#!/usr/bin/perl
use strict;
use warnings;

my $index = 1;
my %seen;

while (<STDIN>) {
    chomp;
    my($start, $key) = /^(\S+\s+\S+)\s+(.+)\s*$/;

    unless ($seen{$key}) {
        # new key detected, we need to open new file
        open(my $fh, '>', "File_${index}.txt")
            or die "can't open new file: $!\n";
        $seen{$key} = $fh;
        $index++;
    }

    my $fh = $seen{$key};
    print $fh "${start}\n";
}

# close files
close $_ foreach (values %seen);

exit 0;

试运行:

$ perl dummy.pl <dummy.txt
$ cat File_1.txt 
XIGO    XIGO_24480
XIGO    XIGO_24481
$ cat File_2.txt 
XOLO    XOLO_Z1E01
XOLO    XOLO_Z1G01
$ cat File_3.txt 
YORK    TYo_0GT393
YORK    TYo_0GT394

注意::出于完整性考虑:如果您的输入具有大约1000个以上的键,则上述解决方案将在标准Linux计算机上遇到too many open files错误。您必须使用ulimit来增加限制,或对数据进行预排序才能使用下面的优化版本。或者在循环结束后将所有数据保留在内存中并写入文件。


编辑:如果您确定键不会在输入文件中重复出现,则可以对其进行优化,例如

my $fh;    
while (<STDIN>) {
    chomp;
    my($start, $key) = /^(\S+\s+\S+)\s+(.+)\s*$/;

    unless ($seen{$key}++) {
        # new key detected, we need to open new file
        if ($fh) {
            close($fh) or die "close: $!\n";
        }
        open($fh, '>', "File_${index}.txt")
            or die "can't open new file: $!\n";
        $index++;
    }

    print $fh "${start}\n";
}

# make sure to close last open file
close($fh) or die "close: $!\n";

我不知道您的实际输入数据是什么样子,但是如果输出顺序无关紧要,则可以使用以下优化版本将输入数据以bash预先排序:

$ sort -t $'\t' -k 3 dummy.txt | perl dummy.pl

EDIT2 ,如果您想保留原始的split()方法:

# remove trailing whitespace
s/\s+$//;
my($FID, $IID, $key) = split('\t', $_);
...
print $fh "${FID}\t${IID}\n";

答案 1 :(得分:1)

尽管未标记awk,但在这种情况下,效果最佳。如果您想尝试,请按以下步骤操作

$ cat victor.txt
XIGO    XIGO_24480  Xigou
XIGO    XIGO_24481  Xigou
XOLO    XOLO_Z1E01  Xoloitzcuintle
XOLO    XOLO_Z1G01  Xoloitzcuintle
YORK    TYo_0GT393  Yorkshire Terrier
YORK    TYo_0GT394  Yorkshire Terrier
$ awk ' { curr=$1; if(prev!=curr) { x++ } print $1, $2, "File_" x ".txt" ; prev=curr } ' victor.txt
XIGO XIGO_24480 File_1.txt
XIGO XIGO_24481 File_1.txt
XOLO XOLO_Z1E01 File_2.txt
XOLO XOLO_Z1G01 File_2.txt
YORK TYo_0GT393 File_3.txt
YORK TYo_0GT394 File_3.txt
$ ls File_1.txt File_2.txt File_3.txt
/bin/ls: cannot access File_1.txt: No such file or directory
/bin/ls: cannot access File_2.txt: No such file or directory
/bin/ls: cannot access File_3.txt: No such file or directory

上面的awk打印我们需要的结果。 awk可以将输出重定向到文件

$ awk ' { curr=$1; if(prev!=curr) { x++ } print $1, $2  > "File_" x ".txt" ; prev=curr } ' victor.txt
$ ls File_1.txt File_2.txt File_3.txt
File_1.txt  File_2.txt  File_3.txt
$ cat File_1.txt
XIGO XIGO_24480
XIGO XIGO_24481
$ cat File_2.txt
XOLO XOLO_Z1E01
XOLO XOLO_Z1G01
$ cat File_3.txt
YORK TYo_0GT393
YORK TYo_0GT394
$