我有一个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;
有什么建议吗?谢谢
答案 0 :(得分:2)
我认为这就是您想要的:
#!/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
$