Какой лучший способ открыть и прочитать файл в Perl?

Пожалуйста, обратите внимание - я не ищу "правильный" способ открыть / прочитать файл или способ, которым я должен открывать / читать файл каждый раз. Мне просто интересно узнать, каким образом большинство людей используют, и, возможно, изучить несколько новых методов одновременно:)*

Очень распространенным блоком кода в моих программах на Perl является открытие файла и чтение или запись в него. Я видел так много способов сделать это, и мой стиль выполнения этой задачи несколько раз менялся с годами. Мне просто интересно, какой лучший (если есть лучший способ) способ сделать это?

Я использовал, чтобы открыть файл, как это:

my $input_file = "/path/to/my/file";
open INPUT_FILE, "<$input_file"  || die "Can't open $input_file: $!\n";

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

Добавление круглых скобок, кажется, исправляет перехват ошибок:

open (INPUT_FILE, "<$input_file")  || die "Can't open $input_file: $!\n";

Я знаю, что вы также можете назначить файловый дескриптор переменной, поэтому вместо использования "INPUT_FILE", как я делал выше, я мог бы использовать $ input_filehandle - это лучше?

Для чтения файла, если он маленький, есть ли что-то плохое в глобализации, например?

my @array = <INPUT_FILE>;

или же

my $file_contents = join( "\n", <INPUT_FILE> );

или вы должны всегда проходить, как это:

my @array;
while (<INPUT_FILE>) {
  push(@array, $_);
}

Я знаю, что в Perl есть много способов добиться чего-то, и мне просто интересно, есть ли предпочтительные / стандартные методы открытия и чтения в файле?

12 ответов

Решение

Универсальных стандартов нет, но есть причины отдавать предпочтение тем или иным. Моя предпочтительная форма это:

open( my $input_fh, "<", $input_file ) || die "Can't open $input_file: $!";

Причины:

  • Вы немедленно сообщаете об ошибках. (Замените "умереть" на "предупредить", если вы этого хотите.)
  • Ваш файловый дескриптор теперь подсчитывается по ссылке, поэтому, если вы его не используете, он будет автоматически закрыт. Если вы используете глобальное имя INPUT_FILEHANDLE, то вам придется закрыть файл вручную, или он останется открытым до выхода из программы.
  • Индикатор режима чтения "<" отделен от файла $ input_file, что повышает удобочитаемость.

Следующее замечательно, если файл маленький, и вы знаете, что хотите все строки:

my @lines = <$input_fh>;

Вы даже можете сделать это, если вам нужно обработать все строки как одну строку:

my $text = join('', <$input_fh>);

Для длинных файлов вы захотите перебирать строки с while или использовать read.

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

use strict;
use warnings;
use Carp;
use English qw( -no_match_vars );
my $data = q{};
{
   local $RS = undef; # This makes it just read the whole thing,
   my $fh;
   croak "Can't open $input_file: $!\n" if not open $fh, '<', $input_file;
   $data = <$fh>;
   croak 'Some Error During Close :/ ' if not close $fh;
}

Вышеуказанное удовлетворяет perlcritic --brutal, который является хорошим способом проверить "лучшие практики":). $input_file здесь все еще не определено, но остальное кошерное.

Необходимость писать "или умирать" везде сводит меня с ума. Мой предпочтительный способ открыть файл выглядит так:

use autodie;

open(my $image_fh, '<', $filename);

Несмотря на то, что печатать очень мало, нужно отметить много важных вещей:

  • Мы используем прагму autodie, что означает, что все встроенные в Perl вызовут исключение, если что-то пойдет не так. Это устраняет необходимость в написании or die ... в вашем коде он создает дружественные, понятные человеку сообщения об ошибках и имеет лексическую область видимости. Это доступно из CPAN.

  • Мы используем версию с тремя аргументами open. Это означает, что даже если у нас есть смешное имя файла, содержащее такие символы, как <, > или же | Perl все равно поступит правильно. В моем руководстве по безопасности Perl в OSCON я показал несколько способов получить 2 аргумента open плохо себя вести. Примечания к этому руководству доступны для бесплатной загрузки с Perl Training Australia.

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

  • Мы используем значимый дескриптор файла. В этом случае похоже, что мы собираемся написать изображение.

  • Дескриптор файла заканчивается _fh, Если мы увидим, что мы используем его как обычный скаляр, то мы знаем, что это, вероятно, ошибка.

Если ваши файлы настолько малы, что возможно чтение всего в память, используйте File:: Slurp. Он читает и записывает полные файлы с очень простым API, а также выполняет всю проверку ошибок, поэтому вам не нужно этого делать.

Нет лучшего способа открыть и прочитать файл. Это неправильный вопрос. Что в файле? Сколько данных вам нужно в любой момент? Вам нужны все данные одновременно? Что вам нужно делать с данными? Вы должны выяснить это, прежде чем думать о том, как вам нужно открыть и прочитать файл.

Что-то, что вы делаете сейчас, вызывает у вас проблемы? Если нет, разве у вас нет проблем лучше решить?:)

Большая часть вашего вопроса - просто синтаксис, и на все это есть ответы в документации по Perl (особенно ( perlopentut). Возможно, вам также захочется выбрать Learning Perl, который отвечает на большинство проблем, возникающих в вашем вопросе.

Удачи,:)

Это правда, что существует столько же лучших способов открыть файл в Perl, сколько существует

$files_in_the_known_universe * $perl_programmers

... но все равно интересно посмотреть, кто обычно так делает. Моя предпочитаемая форма слэпинга (чтение всего файла сразу):

use strict;
use warnings;

use IO::File;

my $file = shift @ARGV or die "what file?";

my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
my $data = do { local $/; <$fh> };
$fh->close();

# If you didn't just run out of memory, you have:
printf "%d characters (possibly bytes)\n", length($data);

И при переходе построчно:

my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
while ( my $line = <$fh> ) {
    print "Better than cat: $line";
}
$fh->close();

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

Для ОО мне нравится:

use FileHandle;
...
my $handle = FileHandle->new( "< $file_to_read" );
croak( "Could not open '$file_to_read'" ) unless $handle;
...
my $line1 = <$handle>;
my $line2 = $handle->getline;
my @lines = $handle->getlines;
$handle->close;

Я когда-то использовал

open (FILEIN, "<", $inputfile) or die "...";
my @FileContents = <FILEIN>;
close FILEIN;

шаблон регулярно. В настоящее время я использую File::Slurp для небольших файлов, которые я хочу полностью сохранить в памяти, и Tie::File для больших файлов, которые я хочу масштабировать, и / или файлы, которые я хочу изменить на месте.

Прочитать весь файл $file в переменную $text одной строкой

$text = do {local(@ARGV, $/) = $file ; <>};

или как функция

$text = load_file($file);
sub load_file {local(@ARGV, $/) = @_; <>}

|| Оператор имеет более высокий приоритет, поэтому он сначала оценивается перед отправкой результата в "open"... В упомянутом коде вместо этого используйте оператор "или", и у вас не возникнет такой проблемы.

open INPUT_FILE, "<$input_file"
  or die "Can't open $input_file: $!\n";

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

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

Один из советов, которые я получил из одной из глав "Прагматического программиста" (Хант и Томас), заключается в том, что вам может потребоваться, чтобы скрипт сохранил для вас резервную копию файла, прежде чем приступить к нарезке и нарезанию кубиками.

Дамиан Конвей делает это так:

$data = readline!open(!((*{!$_},$/)=\$_)) for "filename";

Но я не рекомендую это вам.

Другие вопросы по тегам