Печать Perl для отдельных файлов - PullRequest
0 голосов
/ 09 мая 2018

У меня есть текстовый файл, в котором перечислены служба, устройство и фильтр, здесь я перечисляю только 3 примера:

service1 device04 filter9
service2 device01 filter2
service2 device10 filter11

Я написал Perl-скрипт, который выполняет итерацию по файлу, и затем должен вывести device=device filter=filter в файл с именем, соответствующим сервису, которому он принадлежит, но если строка содержит дублирующий фильтр, она должна добавить устройства в тот же файл, разделенных точками с запятой. Глядя на приведенный выше пример, мне нужен результат:

service1.txt

device=device04 filter=filter9

service2.txt

device=device01 filter=filter2 ; device=device10 filter=filter11

Вот мой код:

use strict;
use warnings qw(all);
open INPUT, "<", "file.txt" or die $!;
my @Input = <INPUT>;

foreach my $item(@Input) {
     my ($serv, $device, $filter) = split(/ /, $item);
     chomp ($serv, $device, $filter);
     push my @arr, "device==$device & filter==$filter";

     open OUTPUT, ">>", "$serv.txt" or die $!;
     print OUTPUT join(" ; ", @arr);
     close OUTPUT;
}

Проблема, с которой я столкнулся, заключается в том, что созданы и service1.txt, и service2.txt, но все мои результаты неверны, см. Мой текущий результат:

service1.txt

device==device04 filter==filter9

service2.txt

device==device04 filter==filter9 ; device==device01 filter==filter2device==device04 filter==filter9 ; device==device01 filter==filter2 ; device==device10 filter==filter11

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

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Чтобы каждый сервис имел свой собственный файл, в котором накапливаются данные, необходимо различать для каждой строки, в какой файл его печатать.

Затем откройте новый сервисный файл, когда сервис без такового встречается, выполнимо, так как в комментарии не так много, как указано. Это может быть организовано хеш-сервисом => дескриптор файла.

use warnings;
use strict;
use feature 'say';

my $file = shift @ARGV || 'data.txt';    
my %handle;

open my $fh, '<', $file or die "Can't open $file: $!";

while (<$fh>) {
    my ($serv, $device, $filter) = split;

    if (exists $handle{$serv}) {
        print { $handle{$serv} } " ; device==$device & filter==$filter";
    }   
    else {
        open my $fh_out, '>', "$serv.txt" or do {
            warn "Can't open $serv.txt: $!";
            next;
        };
        print $fh_out "device==$device & filter==$filter";
        $handle{$serv} = $fh_out;
    }   
}

say $_ '' for values %handle;  # terminate the line in each file

close $_ for values %handle;

Для ясности код печатает почти одинаково в обоих случаях, что, безусловно, можно сделать чище. Это было проверено только с предоставленными образцами данных и дает желаемый результат.

Заметьте, что когда нужно проверить дескриптор файла, нам нужно { }. См. этот пост , например.

Комментарии к исходному коду (в приведенном выше коде)

  • Использовать лексические файловые дескрипторы (my $fh) вместо typeglobs (FH)

  • Не читайте весь файл сразу, если для этого нет особой причины

  • split имеет хорошие значения по умолчанию, split ' ', $_, где ' ' разделяется на пробел и также отбрасывает начальные и конечные пробелы. (И тогда нет необходимости chomp в этом случае.)

  • Другой вариант - сначала собрать данные для каждой службы, так же, как попытка OP, но снова использовать хеш (service => arrayref / string with data) и распечатать в конце. Но я не вижу причин не печатать по ходу дела, поскольку вам потребуется та же логика, чтобы решить, когда нужно добавить ;.

0 голосов
/ 09 мая 2018

Ваш код выглядит довольно perl4-иш, но это не проблема. Как указал MrTux, вы сбиваете с толку сбор и распространение ваших данных. Я реорганизовал это, чтобы использовать хэш в качестве промежуточного контейнера с именем службы в качестве ключей. Обратите внимание, что при этом не будут накапливаться результаты по нескольким вызовам (поскольку он использует «>», а не «>>»).

use strict;
use warnings qw(all);
use File::Slurp qw/read_file/;
my @Input = read_file('file.txt', chomp => 1);

my %store = (); # Global container
# Capture
foreach my $item(@Input) {
     my ($serv, $device, $filter) = split(/ /, $item);
     push @{$store{$serv}}, "device==$device & filter==$filter";
}
# Write out for each service file
foreach my $k(keys %store) {
    open(my $OUTPUT, ">", "$k.txt") or die $!;
    print $OUTPUT join(" ; ", @{$store{$k}});
    close( $OUTPUT );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...