Quanti modi diversi ci sono per concatenare due file riga per riga usando Perl?

StackOverflow https://stackoverflow.com/questions/1636755

  •  06-07-2019
  •  | 
  •  

Domanda

Supponiamo che file1 assomigli a questo:

bye bye
hello
thank you

E file2 è simile al seguente:

chao
hola
gracias

L'output desiderato è questo:

bye bye chao
hello hola
thank you gracias

Io stesso ho già escogitato cinque diversi approcci per risolvere questo problema. Ma penso che ci debbano essere più modi, probabilmente più concisi e più eleganti, e spero di poter imparare cose più interessanti :)

Quanto segue è quello che ho provato finora, in base a ciò che ho imparato dalle molte soluzioni dei miei precedenti problemi. Inoltre, sto cercando di digerire o interiorizzare le conoscenze che ho acquisito dal libro di Llama.

Codice 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";
}

Codice 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];
}

Codice 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;

Codice 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;
}

Codice 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";
}

Ho provato qualcosa del genere:

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

Ma Perl mi ha dato un avviso di errore sintattico. Ero frustrato. Ma possiamo eseguire due foreach loop contemporaneamente?

Grazie, come sempre, per eventuali commenti, suggerimenti e ovviamente per la generosa condivisione del codice :)

È stato utile?

Soluzione

Funziona con qualsiasi numero di file:

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;

Altri suggerimenti

Il modo più elegante non prevede affatto perl:

paste -d' ' file1 file2

Se fossi un golfista, potrei riscrivere @ Risposta di FM come:

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

che potresti essere in grado di trasformare in un one-liner ma che è semplicemente malvagio. ; -)

Bene, eccolo qui, con meno di 100 caratteri:

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

Se è giusto bere (e perché diamine non & # 8212; noi sono alla ricerca di modi diversi), penso di aver scoperto il percorso la follia:

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

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

Output:

bye bye chao
hello hola
thank you gracias

Un'alternativa più semplice al tuo codice 5 che consente un numero arbitrario di righe e non importa se i file hanno un numero diverso di righe (hat tip @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";
}

E, senza utilizzare alcun modulo esterno:

#!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";
}

Se si desidera concatenare solo le righe visualizzate in entrambi i file:

#!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";
}

Semplice con un controllo minimo degli errori:

#!/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";
}

E no, non puoi eseguire due loop contemporaneamente nello stesso thread, dovresti fork, ma non sarebbe garantito che funzionino in modo sincrono.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top