Как разделить длинный файл и распечатать его на разные текстовые файлы с помощью Perl? - PullRequest
0 голосов
/ 12 февраля 2019

У меня есть текстовый файл с длинным списком из 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  

Мне нужны выходные текстовые файлы с числовыми тегами.Список разделен на 3 столбца.

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 голосов
/ 12 февраля 2019

Хотя 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
$
0 голосов
/ 12 февраля 2019

Я думаю, это то, что вы ищете:

  • читать строки ввода из 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

ПРИМЕЧАНИЕ: для полноты картины: приведенное выше решение приведет к ошибке too many open files на стандартном компьютере с Linux, если ваш ввод более ~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";
...