В Perl, как я могу прочитать весь файл в строку?

Я пытаюсь открыть .html-файл в виде одной большой длинной строки. Вот что у меня есть:--4-->

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"

посмотреть переменные, связанные с filehandles на perldoc perlvar и perldoc -f local.

кстати, если вы можете поместить свой скрипт на сервере, вы можете иметь все модули, которые вы хотите. См.как сохранить собственный модуль / библиотеку каталог?.

кроме того, Путь::Class:: File позволяет хлебать и изрыгать.

Путь::Крошечная дает еще больше методов удобства, таких как 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 с тремя аргументами. Это намного безопаснее, чем старые версии с двумя (или одним) аргументами. Также обратите внимание на использование лексического filehandle. Лексические файловые ручки лучше, чем старые варианты без слов, по многим причинам. Мы пользуемся одним из них здесь: они закрываются, когда выходят за пределы видимости.


С File:: Slurp:

use File::Slurp;
my $text = read_file('index.html');

да, даже вы можете использовать CPAN.


все сообщения, чуть не идиоматические. Идиома:

open my $fh, '<', $filename or die "error opening $filename: $!";
my $data = do { local $/; <$fh> };

в основном, нет необходимости устанавливать $ / to 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, которые позволяют привязать массив к файлу, чтобы получить доступ к элементу, массив фактически обращается к соответствующей строке в файле.

вы можете узнать всю содержимое filehandle в скаляр.

{
local(*INPUT, $/);
open (INPUT, $file)     || die "can't open $file: $!";
$var = <INPUT>;
}

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

$var = do { local $/; <INPUT> };

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

read( INPUT, $var, -s INPUT );

третий аргумент проверяет размер байтов данных на входной файловой ручке и считывает это количество байтов в буфер $var.


простой способ:

while (<FILE>) { $document .= $_ }

другой способ-изменить разделитель входной записи"$/". Вы можете сделать это локально в голом блоке, чтобы избежать изменения глобального разделителя записей.

{
    open(F, "filename");
    local $/ = undef;
    $d = <F>;
}

либо set $/ to undef (см. ответ jrockway) или просто объедините все строки файла:

$content = join('', <$fh>);

рекомендуется использовать скаляры для filehandles на любой версии Perl, которая поддерживает его.


еще один возможный вариант:

open my $fh, '<', "filename";
read $fh, my $string, -s $fh;
close $fh;

вы получаете только первую строку от алмазного оператора <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.

это, по крайней мере один побочный эффект, который стоил мне часа найти: он не закрывает неявный дескриптор файла должным образом (так как он не вызывает 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, вы говорите, что нет разделителя полей. Это называется режимом "slurp".

другие решения, такие как undef $/ и local $/ (но не my $/) redeclare $ / и, таким образом, производят тот же эффект.


вы можете просто создать подпрограмму:

#Get File Contents
sub gfc
{
    open FC, @_[0];
    join '', <FC>;
}

Я не знаю, хорошая ли это практика, но я использовал это:

($a=<F>);

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

$x=`cat /tmp/foo`;    # note backticks, qw"cat ..." also works

вы можете использовать cat в Linux:

@file1=\`cat /etc/file.txt\`;