В Perl, как я могу прочитать весь файл в строку?
Я пытаюсь открыть .html-файл в виде одной большой длинной строки. Вот что у меня есть:
open(FILE, 'index.html') or die "Can't read file 'filename' [$!]n";
$document = <FILE>;
close (FILE);
print $document;
что приводит к:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN
однако, я хочу, чтобы результат выглядел так:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
таким образом, я могу искать весь документ более легко.
17 ответов:
добавить:
local $/;
перед чтением из дескриптора файла. Смотрите как я могу прочитать весь файл сразу? или
$ perldoc -q "entire file"посмотреть переменные, связанные с файловыми хэндлами на
perldoc perlvar
иperldoc -f local
.кстати, если вы можете поместить свой скрипт на сервере, вы можете иметь все модули, которые вы хотите. Смотрите как сохранить свой собственный модуль / библиотеку справочник?.
кроме того, Путь::Класс::Файл позволяет хлебать и изрыгать.
Path:: Tiny дает еще больше удобных методов, таких как
slurp
,slurp_raw
,slurp_utf8
а такжеspew
коллегами.
Я бы сделал так:
my $file = "index.html"; my $document = do { local $/ = undef; open my $fh, "<", $file or die "could not open $file: $!"; <$fh>; };
обратите внимание на использование версии open с тремя аргументами. Это гораздо безопаснее, чем старые версии с двумя (или одним) аргументами. Также обратите внимание на использование лексического файлового хэндла. Лексические ручки файлов лучше, чем старые варианты без слов, по многим причинам. Мы пользуемся одним из них здесь: они закрываются, когда выходят за пределы видимости.
С File:: Slurp:
use File::Slurp; my $text = read_file('index.html');
все сообщения, чуть не идиоматические. Идиома такова:
open my $fh, '<', $filename or die "error opening $filename: $!"; my $data = do { local $/; <$fh> };
в основном, нет необходимости устанавливать $ / в
undef
.
С perlfaq5: как я могу прочитать весь файл сразу?:
вы можете использовать модуль File::Slurp, чтобы сделать это за один шаг.
use File::Slurp; $all_of_it = read_file($filename); # entire file in scalar @all_lines = read_file($filename); # one line per element
обычный подход Perl для обработки всех строк в файле заключается в том, чтобы делать это по одной строке за раз:
open (INPUT, $file) || die "can't open $file: $!"; while (<INPUT>) { chomp; # do something with $_ } close(INPUT) || die "can't close $file: $!";
это чрезвычайно более эффективно, чем чтение всего файла в память в виде массива строк, а затем обработка его по одному элементу за раз, что часто-если не почти всегда-неправильный подход. Всякий раз, когда вы видите, что кто-то делает это:
@lines = <INPUT>;
вы должны думать долго и трудно о том, почему вам нужно все загружается сразу. Это не масштабируемое решение. Вы также можете найти более интересным использовать стандартный модуль Tie::File или привязки $DB_RECNO модуля DB_File, которые позволяют привязать массив к файлу, чтобы получить доступ к элементу массив фактически обращается к соответствующей строке в файле.
вы можете узнать всю содержимое ручки файла в скаляр.
{ local(*INPUT, $/); open (INPUT, $file) || die "can't open $file: $!"; $var = <INPUT>; }
это временно отменяет ваш разделитель записей и автоматически закрывает файл при выходе из блока. Если файл уже открыт, просто используйте это:
$var = do { local $/; <INPUT> };
для обычных файлов, вы также можете воспользоваться функцией чтения.
read( INPUT, $var, -s INPUT );
третий аргумент проверяет размер байта данных на входном файловом хэндле и считывает это количество байтов в буфер $var.
самый простой способ-это:
while (<FILE>) { $document .= $_ }
другой способ-изменить разделитель входных записей"$/". Вы можете сделать это локально в голом блоке, чтобы избежать изменения глобального разделителя записей.
{ open(F, "filename"); local $/ = undef; $d = <F>; }
либо set
$/
toundef
(см. ответ jrockway) или просто объедините все строки файла:$content = join('', <$fh>);
рекомендуется использовать скаляры для файловых хэндлов на любой версии Perl, которая его поддерживает.
вы получаете только первую строку от алмазного оператора
<FILE>
потому что вы оцениваете его в скалярном контексте:$document = <FILE>;
в контексте списка / массива оператор diamond возвращает все строки файла.
@lines = <FILE>; print @lines;
Я бы сделал это самым простым способом, чтобы любой мог понять, что происходит, даже если есть более умные способы:
my $text = ""; while (my $line = <FILE>) { $text .= $line; }
open f, "test.txt" $file = join '', <f>
<f>
- возвращает массив строк из файла (если$/
значение по умолчанию"\n"
) и нажмитеjoin ''
буду придерживаться этого массива.
это скорее предложение о том, как не сделать это. У меня просто было плохое время, чтобы найти ошибку в довольно больших приложений на Perl. Большинство модулей имели свои собственные конфигурационные файлы. Чтобы прочитать файлы конфигурации в целом, я нашел эту единственную строку Perl где-то в Интернете:
# Bad! Don't do that! my $content = do{local(@ARGV,$/)=$filename;<>};
он переназначает разделитель строк, как объяснялось ранее. Но он также переназначает STDIN.
это, по крайней мере один побочный эффект, который стоил мне часа find: он не закрывает неявный дескриптор файла должным образом (так как он не вызывает
close
на всех).например, делая это:
use strict; use warnings; my $filename = 'some-file.txt'; my $content = do{local(@ARGV,$/)=$filename;<>}; my $content2 = do{local(@ARGV,$/)=$filename;<>}; my $content3 = do{local(@ARGV,$/)=$filename;<>}; print "After reading a file 3 times redirecting to STDIN: $.\n"; open (FILE, "<", $filename) or die $!; print "After opening a file using dedicated file handle: $.\n"; while (<FILE>) { print "read line: $.\n"; } print "before close: $.\n"; close FILE; print "after close: $.\n";
результаты:
After reading a file 3 times redirecting to STDIN: 3 After opening a file using dedicated file handle: 3 read line: 1 read line: 2 (...) read line: 46 before close: 46 after close: 0
странно то, что счетчик строк
$.
увеличивается для каждого файла по отдельности. Он не сбрасывается, и он не содержит количество строк. И он не сбрасывается до нуля при открытии другого файла, пока не будет прочитана хотя бы одна строка. В моем случае, я делал что-то вроде это:while($. < $skipLines) {<FILE>};
из-за этой проблемы условие было ложным, потому что счетчик строк не был сброшен должным образом. Я не знаю, если это ошибка или просто неправильный код... Также звонит
close;
Одерclose STDIN;
не помогает.Я заменил этот нечитаемый код с помощью open, string concatenation и close. Однако решение, опубликованное Брэдом Гилбертом, также работает, поскольку вместо него используется явный дескриптор файла.
три строки в начале могут быть заменить на:
my $content = do{local $/; open(my $f1, '<', $filename) or die $!; my $tmp1 = <$f1>; close $f1 or die $!; $tmp1}; my $content2 = do{local $/; open(my $f2, '<', $filename) or die $!; my $tmp2 = <$f2>; close $f2 or die $!; $tmp2}; my $content3 = do{local $/; open(my $f3, '<', $filename) or die $!; my $tmp3 = <$f3>; close $f3 or die $!; $tmp3};
который правильно закрывает дескриптор файла.
использовать
$/ = undef;
до
$document = <FILE>;
.$/
- это входной разделитель записей, который является новой строкой по умолчанию. Переопределив его вundef
, вы говорите, что нет разделителя полей. Это называется режим "хлебать".другие решения, такие как
undef $/
иlocal $/
(но неmy $/
) redeclare $ / и, таким образом, производят тот же эффект.
вы можете просто создать подпрограмму:
#Get File Contents sub gfc { open FC, @_[0]; join '', <FC>; }