2013-01-18 12 views
8

Przechwytuję kilka kolumn z pliku zaznaczonego w tabulatorze w Perlu. Pierwsza linia pliku jest zupełnie inna niż pozostałe linie, więc chciałbym pominąć tę linię tak szybko i sprawnie, jak to tylko możliwe.Najlepszy sposób na pominięcie nagłówka podczas czytania z pliku tekstowego w Perlu?

To jest to, co do tej pory miałem.

my $firstLine = 1; 

while (<INFILE>){ 
    if($firstLine){ 
     $firstLine = 0; 
    } 
    else{ 
     my @columns = split (/\t+/); 
     print OUTFILE "$columns[0]\t\t$columns[1]\t$columns[2]\t$columns[3]\t$columns[11]\t$columns[12]\t$columns[15]\t$columns[20]\t$columns[21]\n"; 
    } 
} 

Czy jest lepszy sposób to zrobić, być może bez $ firstLine? LUB czy istnieje sposób na rozpoczęcie czytania INFILE bezpośrednio z linii 2?

Z góry dziękuję!

+5

Jako marginesie, plasterki tablicy i dołącz wyeliminuje wiele tego powtarzającego się kodu. 'print OUTFILE" $ columns [0] \ t \ t "; print OUTFILE join ("\ t", @kolumny [1,2,3,11,12,15,20,21]); print OUTFILE "\ n"; ' – Schwern

+0

Będę musiał sprawdzić dołączenie. Jestem nowy w Perlu. Dzięki! – New2Perl

+0

Oczyściłem to jeszcze bardziej: 'print OUTFILE" $ columns [0] \ t \ t ". join ("\ t", @columns [1,2,3,11,12,15,20,21]). "\ n"; ' – New2Perl

Odpowiedz

26

Zdobądźmy trochę danych na ten temat. I na podstawie porównania technik niesłyszących ...

#!/usr/bin/env perl 

sub flag_in_loop { 
    my $file = shift; 

    open my $fh, $file; 

    my $first = 1; 
    while(<$fh>) { 
     if($first) { 
      $first = 0; 
     } 
     else { 
      my $line = $_; 
     } 
    } 

    return; 
} 

sub strip_before_loop { 
    my $file = shift; 

    open my $fh, $file; 

    my $header = <$fh>; 
    while(<$fh>) { 
     my $line = $_; 
    } 

    return; 
} 

sub line_number_in_loop { 
    my $file = shift; 

    open my $fh, $file; 

    while(<$fh>) { 
     next if $. < 2; 

     my $line = $_; 
    } 

    return; 
} 

sub inc_in_loop { 
    my $file = shift; 

    open my $fh, $file; 

    my $first; 
    while(<$fh>) { 
     $first++ or next; 

     my $line = $_; 
    } 

    return; 
} 

sub slurp_to_array { 
    my $file = shift; 

    open my $fh, $file; 

    my @array = <$fh>; 
    shift @array; 

    return; 
} 


my $Test_File = "/usr/share/dict/words"; 
print `wc $Test_File`; 

use Benchmark; 

timethese shift || -10, { 
    flag_in_loop  => sub { flag_in_loop($Test_File); }, 
    strip_before_loop => sub { strip_before_loop($Test_File); }, 
    line_number_in_loop => sub { line_number_in_loop($Test_File); }, 
    inc_in_loop   => sub { inc_in_loop($Test_File); }, 
    slurp_to_array  => sub { slurp_to_array($Test_File); }, 
}; 

Ponieważ jest I/O, które mogą mieć wpływ sił poza zdolnością Benchmark.pm dostosowania do pobiegłem do nich kilka razy i sprawdzone mam takie same wyniki.

/usr/share/dict/words to plik o rozdzielczości 2,4 megapiksela z bardzo krótkimi liniami o długości 240k. Ponieważ nie przetwarzamy linii, długość linii nie powinna mieć znaczenia.

Zrobiłem tylko niewielką ilość pracy w każdej rutynie, aby podkreślić różnicę między technikami. Chciałem wykonać pewną pracę w celu uzyskania realistycznego górnego limitu wydajności, którą zamierzasz uzyskać lub stracić zmieniając sposób czytania plików.

Zrobiłem to na laptopie z dyskiem SSD, ale nadal jest to laptop. Wraz ze wzrostem prędkości we/wy czas procesora staje się coraz ważniejszy. Technika jest jeszcze ważniejsza na maszynie z szybkimi wejściami i wyjściami.

Oto, ile razy każda rutyna odczytuje plik na sekundę.

slurp_to_array:  4.5/s 
line_number_in_loop: 13.0/s 
inc_in_loop:   15.5/s 
flag_in_loop:  15.8/s 
strip_before_loop: 19.9/s 

jestem wstrząśnięci, że my @array = <$fh> jest najwolniejsze przez ogromny margines. Pomyślałbym, że będzie to najszybsze, biorąc pod uwagę, że cała praca dzieje się wewnątrz interpretera perla. Jednak to jedyny, który przydziela pamięć do przechowywania wszystkich linii i prawdopodobnie odpowiada za opóźnienie wydajności.

Używanie $. to kolejna niespodzianka.Być może to jest koszt dostępu do magii globalnej, a może jej porównanie liczbowe.

I, zgodnie z przewidywaniami analizy algorytmicznej, umieszczenie kodu sprawdzania nagłówka poza pętlą jest najszybsze. Ale nie za dużo. Prawdopodobnie nie wystarczy, aby się martwić, jeśli używasz dwóch kolejnych najszybszych.

+0

Wow! Bardzo interesujące! – New2Perl

+0

+1, bardzo fajny ... – Guru

+0

+1. Rzeczywiście ciekawe porównanie. – flesk

19

można po prostu przypisać mu zmienną manekina na 1 raz:

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

open my $fh, '<','a.txt' or die $!; 

my $dummy=<$fh>; #First line is read here 
while(<$fh>){ 
     print ; 
} 
close($fh); 
+0

fh nie powinien mieć wartości $, ponieważ jest to uchwyt pliku. Ale to wygląda na najbardziej wydajne rozwiązanie. Dzięki! – New2Perl

+3

Jest to uchwyt pliku leksykalnego; to jest obecnie preferowane. –

+0

TIL. Dzięki za informację! – New2Perl

2

można odczytać pliku w uchwyt pliku, a następnie można użyć tablicę lub pętli while iteracyjne nad liniami. dla pętli while @Guru ma dla Ciebie rozwiązanie. do tablicy, to byłoby jak poniżej:

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

open (my $fh, '<','a.txt') or die "cant open the file: $! \n"; 
my @array = <$fh>; 

my $dummy = shift (@array); << this is where the headers are stored. 

foreach (@array) 
{ 
    print $_."\n"; 
} 
close ($fh); 
+0

Zapisując cały plik w tablicy, potencjalnie zużywa on dużo pamięci. – Schwern

+0

Jest to o wiele bardziej wydajne niż sekwencyjne czytanie pliku z dysku. A "dużo pamięci" było ważne 15 lat temu. –

7

Zawsze używam $. (bieżący numer linii) do osiągnięcia tego celu:

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

open my $fh, '<', 'myfile.txt' or die "$!\n"; 

while (<$fh>) { 
    next if $. < 2; # Skip first line 

    # Do stuff with subsequent lines 
} 
+0

Jako ogólna technika jest to mniej wydajne, ponieważ pętla musi teraz, przy każdej iteracji, wykonywać dodatkową kontrolę. To również zaśmieca pętlę. – Schwern

+0

Utrata wydajności jest podana, ale jest na tyle znikoma, że ​​jest tego warta, ponieważ wygląda na bardziej stabilną. Jeśli czujesz, że zatacza się w pętli, musi to być kwestia gustu. – flesk

+0

"Clutters up the loop" oznacza, że ​​dodaje do ilości kodu, który musisz zrozumieć, aby wiedzieć, co dzieje się w pętli, ale dotyczy to tylko pierwszej iteracji. Porównuję to do najlepszego przypadku Guru, który ma umieścić ten kod poza pętlą, a nie OP. – Schwern

0

Twój kod będzie prawdopodobnie bardziej elegancki w tej formie:

my $first; 
while (...) { 
    $first++ or next; 

    # do whatever you want 
}; 

Ale nadal jest w porządku. Odpowiedź @ Guru jest lepsza pod względem cykli procesora, ale ja/o zwykle zużywa o rząd wielkości więcej niż jeden, jeśli.

0

Miałem podobne pytanie/problem. Moje rozwiązanie było następujące - zarówno dla rozpakowanych lub zgzipowanymi plików:

print STDERR "\nReading input file...\n"; 
if ($file =~ /.gz$/) { 
    open(IN, "gunzip -c $file | grep -v '##' |") or die " *** ERROR ***  Cannot open pipe to [ $file ]!\n"; 
    } else { 
     open(IN, "cat $file | grep -v '##' |") or die " *** ERROR ***  Cannot open [ $file ]!\n"; 
} 

nie wiem o benchmarkingu, ale to działa dobrze dla mnie.

Best,

Sander

Powiązane problemy