В 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">

Таким образом, я могу искать весь документ легче.

20 ответов

Решение

Добавлять:

 local $/;

перед чтением из дескриптора файла. См. Как я могу прочитать весь файл сразу?, или же

 $ perldoc -q "весь файл" 

См. Переменные, связанные с файловыми дескрипторами в perldoc perlvar а также perldoc -f local,

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

Кроме того, Path:: Class:: File позволяет хлебать и извергать.

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');

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

Вы можете прочитать все содержимое файлового дескриптора в скаляр.

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

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

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

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

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

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

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

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

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

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

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

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

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

Использование

 $/ = undef;

до $document = <FILE>;, $/ является разделителем входных записей, который по умолчанию является новой строкой. Переопределив его undef, вы говорите, что нет разделителя полей. Это называется режимом "slurp".

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

Еще один возможный способ:

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

Вы получаете только первую строчку от алмазного оператора <FILE> потому что вы оцениваете это в скалярном контексте:

$document = <FILE>; 

В контексте списка / массива оператор diamond вернет все строки файла.

@lines = <FILE>;
print @lines;

Это скорее предложение о том, как НЕ делать этого. Я только что плохо нашел ошибку в довольно большом 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 string и 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};

который правильно закрывает дескриптор файла.

open f, "test.txt"
$file = join '', <f>

<f> - возвращает массив строк из нашего файла (если $/ имеет значение по умолчанию "\n") а потом join '' вставит этот массив в.

Я бы сделал это самым простым способом, чтобы каждый мог понять, что происходит, даже если есть более разумные способы:

my $text = "";
while (my $line = <FILE>) {
    $text .= $line;
}

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

($a=<F>);

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

      use IO::File;
my $content = join '', IO::File->new($filename)->getlines;

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

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

Еще один подход:

      sub configure_logger ( ) {
  my @configuration = DATA -> getlines;
  my $configuration = join( "\n", @configuration );
  Log::Log4perl -> init( \$configuration );
}

configure_logger();

my $logger = Log::Log4perl -> get_logger;

Здесь мы читаем дескриптор файла в массив (сgetlinesметод), а затем преобразовать значение массива в строку (используяjoin).

getlines — это встроенный в Perl метод дескриптора файла, полученный из автоматически загружаемого класса IO::Handle , который позволяет нам рассматривать дескрипторы файлов как объекты.

DATA — это специальный дескриптор файла в Perl, но он также может ссылаться на любой другой.

      use Path::Tiny qw( path );
 
my $file = 'data.txt';
my $data = path($file)->slurp_utf8;

Режим Slurp — чтение файла за один шаг: https://perlmaven.com/slurp

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

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

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

@file1=\`cat /etc/file.txt\`;
Другие вопросы по тегам