Функциональный параметр для вывода в Perl - PullRequest
0 голосов
/ 16 февраля 2012

Я хотел удалить пробелы из нескольких файлов данных через цикл for ниже.Я вставил цикл for в функцию.Функция может читать файлы входных данных, но печать выходных файлов работает неправильно, если я не сбрасываю переменную ($new_data) после каждой печати в файл.В противном случае более ранние данные добавляются к более поздним данным.Кроме того, есть ли проблема, если те же файлы, что и вход и выход, так как я не буду использовать входной файл позже?

Pass @row для чтения из inputFile и $ new_data для записи в outputFile

$dir = '/****/';

$inputFileSpring = $dir . "SpringSIMS.dat";
$inputFileSummer = $dir . "SummerSIMS.dat";
$inputFileFall = $dir . "FallSIMS.dat";

$outputFileSpring = $dir . "Spring.dat";
$outputFileSummer = $dir . "Summer.dat";
$outputFileFall = $dir . "Fall.dat";

#Read Spring SIMS Data
open (NOTE, "$inputFileSpring" || die "Could not open $inputFileSpring\n");
processFile(@row=<NOTE>);
close(NOTE);

#Write Spring Data
open(NOTE, ">$outputFileSpring" || die "Could not open $inputFileSpring\n");
print NOTE $new_data;
close(NOTE);
reset('new_data');

#Read Summer SIMS Data
open (NOTE, "$inputFileSummer" || die "Could not open $inputFileSummer\n");
processFile(@row=<NOTE>);
close(NOTE);

#Write Summer Data
open(NOTE, ">$outputFileSummer" || die "Could not open $inputFileSummer\n");
print NOTE $new_data;
close(NOTE);
reset('new_data');

#Read Fall SIMS Data
open (NOTE, "$inputFileFall" || die "Could not open $inputFileFall\n");
processFile(@row=<NOTE>);
close(NOTE);

#Write Fall Data
open(NOTE, ">$outputFileFall" || die "Could not open $inputFileFall\n");
print NOTE $new_data;
close(NOTE);
reset('new_data');


sub processFile
{
    for $row(@row) {
        chop($row);
        @field = split(/\|/, $row);
        for ($i=0; $i<@field; $i++) {
            if ($field[$i] =~ /^ /)
            {
                $field[$i] = " ";
            }
            else
            {
                $field[$i] =~ s/ *$//g;
            }
            $new_data .= $field[$i] . "|";
        }
        $lastchar = chop($new_data);
        if (@field == 15) {
            $new_data .= "|0";
        }
        $new_data .= "\n";
    }
    # return $new_data;
} # END  sub processFile

exit;

Ответы [ 3 ]

3 голосов
/ 16 февраля 2012

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

Если вы используете лексическую область видимости для своих переменных и передаете аргументы подпрограммам, вы никогда не заметите подобные проблемы.Например:

my $foo = process($bar);

sub process {
    my $arg = shift;
    my $value = ....;
    return $value;
}

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

Не уверен, для чего нужна ваша переменная $last_char, поэтому я оставил ее каклексический.Я также ничего не сделал с кодом в вашей подпрограмме, кроме исправления зверского отступа.Заметные изменения в вашем коде:

  • Использование strict и warnings!
  • Используйте возвращаемое значение, возвращая значение переменной с лексической областью
  • Передачааргументы подпрограмм
  • chop -> chomp.В принципе, вам никогда не следует использовать chop.
  • Использование списка базовых имен для построения имен файлов вместо повторения похожих имен
  • Использование трех аргументов open, с явным режимом и лексическим дескриптором файла.

Примечание: Вы должны никогда, никогда писать Perl-код без использования use strict; use warnings;.Нет смысла не использовать их: вы будете тратить больше времени только на поиск простых ошибок.

Примечание № 2: Непроверенный код

use strict;
use warnings;

my @seasons = ("Spring", "Summer", "Fall");

for my $season (@seasons) {
    my $input  = $season . "SIMS.dat";
    my $output = $season . ".dat";
    output_data($input, $output);
}

sub processFile {
     my $file = shift;
     open my $fh, '<', $file or die "$file: $!";
     while (my $row = <$fh>) {
         chomp $row;  # NOTE: never use chop, use chomp instead
         my @field = split(/\|/, $row);
         for (my $i=0; $i<@field; $i++){
             if ($field[$i] =~ /^ /) {
                 $field[$i] = " ";
             } else {
                 $field[$i] =~ s/ *$//g;
             }
         }
         my $new_data = join "|", @field;
         if(@field == 15) {
             $new_data .= "|0";
         }
         $new_data .= "\n";
     }
     return $new_data;
}

sub output_data {
    my ($input, $output) = @_;
    open my $fh, '>', $output or die "$output: $!";
    print $fh processFile($input);
}

ETA: Теперь, глядя на ваш код подпрограммы, мне приходят в голову следующие оптимизации:

$new_data .= $field[$i] . "|";
....
my $lastchar = chop($new_data);

Нет.Вместо этого используйте join:

$new_data = join "|", @field;

Эта часть:

 if ($field[$i] =~ /^ /) {
     $field[$i] = " ";
 } else {
     $field[$i] =~ s/ *$//g;
 }

... либо изменит первое поле на один пробел " ",если первый символ является пробелом, или он удалит пробелы из конца строки.Это действительно что вы хотите?Т.е. " foo" будет изменено на " " (пробел).

Я бы предположил, что вы ищете что-то вроде:

$field[$i] =~ s/^ *//;
$field[$i] =~ s/ *$//;

В этом случае вы можете просто сделать:

for (@field) {
    s/^ *//;
    s/ *$//;
}

Работает, как задумано, потому что $_ имеет псевдоним для каждого элемента в массиве, и они будут изменены регулярным выражением подстановки.Более подробное решение:

for my $value (@field) {
    $value =~ s/^ *//;
    $value =~ s/ *$//;
}

Или, что еще лучше, вы можете включить это в свой оператор split:

my $new_data = join "|", split /\s*\|\s*/, $row;
$new_data =~ s/^ *//;
$new_data =~ s/ *$//;

Или использовать регулярное выражение, которое, вероятно, будет дешевле:

$row =~ s/\s*\|\s*/|/g;
$row =~ s/^ *//;
$row =~ s/ *$//;
my $new_data = $row;
0 голосов
/ 16 февраля 2012

Любое использование функции Perl reset архаично, но ваше использование также некорректно. Если вы хотите стереть содержимое $new_data, просто скажите одно из

$new_data = '';
$new_data = undef;
undef $new_data;

На самом деле reset('new_data') удаляет ВСЕ символы (*), которые начинаются с букв «a», «d», «e», «n», «t», «w» или «_».

(*) - он не будет удалять лексические переменные, но в этом скрипте нет ни одной из них.

0 голосов
/ 16 февраля 2012

Поскольку вы используете переменную $ new_data в качестве глобальной переменной, и все ваши назначения являются добавками, это приводит к тому, что ваши данные собираются без очистки.

Таким образом, чтобы справиться с этим без вызова перезагрузки каждый раз, вы можете поместить очистку в подпрограмму, поэтому каждый раз, когда вы вызываете подпрограмму, она автоматически очищается.

sub processFile 
 {
  $new_data = ""; #this should empty it out
for $row(@row) {
        chop($row);
        @field = split(/\|/, $row);
        for ($i=0; $i<@field; $i++){
            if ($field[$i] =~ /^ /) 
             {
                 $field[$i] = " ";
             }            
            else 
             {
                $field[$i] =~ s/ *$//g;
             }
            $new_data .= $field[$i] . "|";
        }
        $lastchar = chop($new_data);
        if(@field == 15) {
                $new_data .= "|0";
        }
        $new_data .= "\n";
}
# return $new_data;
} # END  sub processFile

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

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