如何通过Perl来分割长的文件和打印输出到不同的文本文件?

问题描述 投票:1回答:2

我有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;

有给我什么建议吗?谢谢

perl text hash split
2个回答
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

注:出于全面考虑:上述解决方案将一个标准的Linux机器上运行到too many open files错误,如果你的投入已经超过1000〜键。你将不得不使用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
投票

虽然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
$
© www.soinside.com 2019 - 2024. All rights reserved.