2009-10-28 5 views
5

Załóżmy plik1 wygląda następująco:Ile różnych sposobów łączenia dwóch plików wiersz po linii przy użyciu Perla?

 
bye bye 
hello 
thank you 

I plik2 wygląda następująco:

 
chao 
hola 
gracias 

Pożądana moc jest taka:

 
bye bye chao 
hello hola 
thank you gracias 

ja już wymyślić pięć różnych podejścia do rozwiązania tego problemu. Ale myślę, że musi być więcej sposobów, prawdopodobnie bardziej zwięzłych i bardziej eleganckich sposobów i mam nadzieję, że mogę się nauczyć więcej fajnych rzeczy :)

Oto, co próbowałem do tej pory, na podstawie tego, czego się nauczyłem od wiele rozwiązań moich poprzednich problemów. Próbuję też przetrawić lub zinternalizować wiedzę, którą zdobyłem z księgi Lamy.

KOD 1

#!perl 
use autodie; 
use warnings; 
use strict; 

open my $file1,'<','c:/file1.txt'; 
open my $file2,'<','c:/file2.txt'; 

while(defined(my $line1 = <$file1>) 
     and defined(my $line2 = <$file2>)){ 
    die "Files are different sizes!\n" unless eof(file1) == eof(file2); 
    $line1 .= $line2; 
    $line1 =~ s/\n/ /; 
    print "$line1 \n"; 
} 

Kod 2:

#!perl 
use autodie; 
use warnings; 
use strict; 

open my $file1,'<','c:/file1.txt'; 
my @file1 = <$file1>; 

open my $file2,'<','c:/file2.txt'; 
my @file2 =<$file2>; 

for (my $n=0; $n<=$#file1; $n++) { 
    $file1[$n] .=$file2[$n]; 
    $file1[$n]=~s/\n/ /; 
    print $file1[$n]; 
} 

KOD 3

#!perl 
use autodie; 
use warnings; 
use strict; 

open my $file1,'<','c:/file1.txt'; 
open my $file2,'<','c:/file2.txt'; 

my %hash; 

while(defined(my $line1 = <$file1>) 
     and defined(my $line2 = <$file2>)) { 
    chomp $line1; 
    chomp $line2; 
    my ($key, $val) = ($line1,$line2); 
    $hash{$key} = $val; 
} 
print map { "$_ $hash{$_}\n" } sort keys %hash; 

Kod 4:

#!perl 
use autodie; 
use warnings; 
use strict; 

open my $file1,'<','c:/file1.txt'; 
open my $file2,'<','c:/file2.txt'; 

while(defined(my $line1 = <$file1>) 
     and defined(my $line2 = <$file2>)) { 
    $line1 =~ s/(.+)/$1 $line2/; 
    print $line1; 
} 

Kod 5:

#!perl 
use autodie; 
use warnings; 
use strict; 

open my $file1,'<','c:/file1.txt'; 
my @file1 =<$file1>; 

open my $file2,'<','c:/file2.txt'; 
my @file2 =<$file2>; 

while ((@file1) && (@file2)){ 
    my $m = shift (@file1); 
    chomp($m); 

    my $n = shift (@file2); 
    chomp($n); 

    $m .=" ".$n; 
    print "$m \n"; 
} 

Próbowałem coś takiego:

foreach $file1 (@file2) && foreach $file2 (@file2) {...} 

Ale Perl dał mi ostrzeżenie o błędzie składniowym. Byłem sfrustrowany. Ale czy możemy uruchamiać jednocześnie dwie pętle foreach?

Dzięki, jak zawsze, za wszelkie uwagi, sugestie i oczywiście dzielić hojny kod :)

+1

Nie piszesz prawie identyczne pytanie niedawno? –

+2

W rzeczywistości brzmi to jak pytanie typu ankiety bez właściwej odpowiedzi, więc prawdopodobnie powinno to być CW. –

+1

@Kinopiko, to pytanie zadałem kilka dni temu "Jak mogę zamienić kolumnę jednego pliku na kolumnę innego przy użyciu Perla?" był trudniejszy niż ten. – Mike

Odpowiedz

12

Działa to dla dowolnej liczby plików:

use strict; 
use warnings; 
use autodie; 

my @handles = map { open my $h, '<', $_; $h } @ARGV; 

while (@handles){ 
    @handles = grep { ! eof $_ } @handles; 
    my @lines = map { my $v = <$_>; chomp $v; $v } @handles; 
    print join(' ', @lines), "\n"; 
} 

close $_ for @handles; 
+0

+1 Dobre wykorzystanie 'eof'. –

+0

BTW, znajduję 'moje @ handle = map {otwórz moje $ h, '<', $ _; $ h} @ARGV; 'preferable. Pozbywasz się tablicy '@ files', której nie używasz w innym miejscu. Chciałbym móc kilkakrotnie powtórzyć twoją odpowiedź. –

+0

@Sinan Dzięki, to jest dobry pomysł (odpowiedź edytowana). – FMc

2

łatwa z minimalnym sprawdzania błędów:

#!/usr/bin/perl -w 

use strict; 

open FILE1, '<file1.txt'; 
open FILE2, '<file2.txt'; 

while (defined(my $one = <FILE1>) or defined(my $twotemp = <FILE2>)){ 
    my $two = $twotemp ? $twotemp : <FILE2>; 
    chomp $one if ($one); 
    chomp $two if ($two); 
    print ''.($one ? "$one " : '').($two ? $two : '')."\n"; 
} 

I nie, nie można uruchomić dwie pętle jednocześnie w tym samym wątku, musisz fork, ale to nie gwarantowałoby, że uruchomi się synchronicznie.

+1

Bez testowania mogę rozpoznać, że oczekuję operatora trójskładnikowego. Ach, to jest fajne. Osobiście kilka razy próbowałem użyć operatora trójskładnikowego do wykonania zadania, ale bez powodzenia. Wielkie dzięki :) – Mike

+0

Testowane teraz. Działa świetnie! Dzięki jeszcze raz! – Mike

+0

Nie działa, jeśli linia wejściowa ma numer zero. Użyj 'defined' w operatory warunkowe. –

2

Łatwiej alternatywa do kodeksu co pozwala na dowolną liczbę wierszy i nie obchodzi mnie, czy pliki mają różną liczbę linii (kapelusz wskazówka @FM):

#!/usr/bin/perl 

use strict; use warnings; 

use File::Slurp; 
use List::AllUtils qw(each_arrayref); 

my @lines = map [ read_file $_ ], @ARGV; 

my $it = each_arrayref @lines; 

while (my @lines = grep { defined and chomp and length } $it->()) { 
    print join(' ', @lines), "\n"; 
} 

i bez korzystania wszelkie moduły zewnętrzne:

#!perl 
use autodie; use warnings; use strict; 

my ($file1, $file2) = @ARGV; 

open my $file1_h,'<', $file1; 
my @file1 = grep { chomp; length } <$file1_h>; 

open my $file2_h,'<', $file2; 
my @file2 = grep { chomp; length } <$file2_h>; 

my $n_lines = @file1 > @file2 ? @file1 : @file2; 

for my $i (0 .. $n_lines - 1) { 
    my ($line1, $line2) = map { 
     defined $_ ? $_ : '' 
    } $file1[$i], $file2[$i]; 
    print $line1, ' ', $line2, "\n"; 
} 

Jeśli chcesz łączyć tylko linie, które pojawiają się w obu plikach:

#!perl 
use autodie; use warnings; use strict; 

my ($file1, $file2) = @ARGV; 

open my $file1_h,'<', $file1; 
my @file1 = grep { chomp; length } <$file1_h>; 

open my $file2_h,'<', $file2; 
my @file2 = grep { chomp; length } <$file2_h>; 

my $n_lines = @file1 < @file2 ? @file1 : @file2; 

for my $i (0 .. $n_lines - 1) { 
    print $file1[$i], ' ', $file2[$i], "\n"; 
} 
+0

Testowany błąd. Perl mówi "Nie można znaleźć listy/Allutils.pm w @INC". Ale wezmę moduł i przetestuję go ponownie. – Mike

+1

+1 dla 'read_file' i' each_array'. Myślałem także o tym podejściu, ale potem zauważyłem twoją odpowiedź w ostatniej chwili. Osoba może łatwo uogólnić to, aby obsłużyć dowolny N plików, jak również. – FMc

+0

@Sanan, dzięki! Moduł zainstalował i przetestował kod ponownie. działa wspaniale, ale jest brzydki problem. Perl pokazuje mi błąd "nie mogę zlokalizować perl58.dll". oczywiście nie może zlokalizować perl58.dll, ponieważ teraz używam Perl 5.10.1. Jak mogę pozbyć się tego fałszywego alarmu bez obniżania mojego Perla? – Mike

10

Najbardziej elegancki sposób nie wiąże perl w ogóle:

paste -d' ' file1 file2 
+2

+1 Uzgodniono, ale myślę, że celem PO jest nauczyć się Perla, pracując nad takimi zabawkowymi programami. –

+0

@mouviciel, to nie wygląda na perla. ale zgadzam się, że to JEST zwięzłe :) – Mike

+0

czy działa tak działający liniowiec Perla? Zastanawiam się. – Mike

7

Gdybym był golfa człowiek, mogę przepisać @FM's answer jako:

($,,$\)=(' ',"\n");@[email protected];open $_,$_ for @_;print 
map{chomp($a=<$_>);$a} @_=grep{!eof $_} @_ while @_ 

, które możesz zmienić w jedno-liniowe, ale to tylko zło. ;-)

Cóż, to jest tutaj, pod 100 znaków:

C:\Temp> perl -le "$,=' ';@[email protected];open $_,$_ for @_;print map{chomp($a =<$_>);$a} @_=grep{!eof $_ }@_ while @_" file1 file2

Jeśli jest OK slurp (i dlaczego nie cholery — my patrząc na różne sposoby) , myślę, że odkryli ścieżkę szaleństwo:

@[email protected];chomp($x[$.-1]{$ARGV}=$_) && eof 
and $.=0 while<>;print "@$_{@_}\n" for @x 

C:\Temp> perl -e "@[email protected];chomp($x[$.-1]{$ARGV}=$_) && eof and $.=0 while<>;print qq{@$_{@_}\n} for @x" file1 file2

wyjściowa:

 
bye bye chao 
hello hola 
thank you gracias 
+0

To nie jest mój zamiar używać tego! Dobra wydajność. – mouviciel

+1

+1 z laserem friggin na głowie! – DVK

+0

@Sinan :) Wielkie dzięki! Ta nikczemna jedna liniówka działa świetnie: perl -le "$, = '"; @ _ = @ ARGV; otwórz $ _, $ _ dla @_; wydrukuj mapę {chomp ($ a = <$_>); $ a} @_ = grep {! eof $ _} @_ podczas gdy @_ "" c: /file1.txt "" c: /file2.txt ". – Mike

Powiązane problemy