Сколько существует различных способов построчного объединения двух файлов с использованием Perl?

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

  •  06-07-2019
  •  | 
  •  

Вопрос

Предположим, файл 1 выглядит следующим образом:

bye bye
hello
thank you

И file2 выглядит примерно так:

chao
hola
gracias

Желаемый результат заключается в следующем:

bye bye chao
hello hola
thank you gracias

Я сам уже придумал пять различных подходов к решению этой проблемы.Но я думаю, что должно быть больше способов, возможно, более кратких и элегантных, и я надеюсь, что смогу узнать больше интересных вещей :)

Ниже приведено то, что я пробовал до сих пор, основываясь на том, что я узнал из множества решений моих предыдущих проблем.Кроме того, я пытаюсь как бы переварить или усвоить знания, которые я почерпнул из книги о Ламе.

Код 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";
}

Код 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];
}

Код 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;

Код 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;
}

Код 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";
}

Я пробовал что-то подобное:

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

Но Perl выдал мне предупреждение о синтаксической ошибке.Я был расстроен.Но можем ли мы запустить два foreach циклы одновременно?

Спасибо, как всегда, за любые комментарии, предложения и, конечно же, за щедрый обмен кодом :)

Это было полезно?

Решение

Это работает для любого количества файлов:

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;

Другие советы

Самый элегантный способ не предполагает perl вообще:

paste -d' ' file1 file2

Если бы я был игроком в гольф, я мог бы переписать Ответ @FM как:

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

который вы могли бы превратить в однострочник, но это просто зло.;-)

Ну, вот оно, менее 100 символов:

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

Если это нормально — хлебать (а почему, черт возьми, нет - мы являются ищу разные пути), я думаю, что нашел путь к безумию.:

@_=@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

Выходной сигнал:

bye bye chao
hello hola
thank you gracias

Более простая альтернатива вашему Код 5 который допускает произвольное количество строк и не заботится о том, имеют ли файлы разное количество строк (подсказка шляпы @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";
}

Причем, без использования каких-либо внешних модулей:

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

Если вы хотите объединить только те строки, которые отображаются в обоих файлах:

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

Простой вариант с минимальной проверкой ошибок:

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

И нет, вы не можете запускать два цикла одновременно в одном потоке, вам придется fork, но это не гарантировало бы синхронной работы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top