Как мне прочитать содержимое файла в скаляр Perl?

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

 my $fileContents;
 if( $md5Con =~ m/\.php$/g ) {
     my $ftp = Net::FTP->new($DB_ftpserver, Debug => 0) or die "Cannot connect to some.host.name: $@";
     $ftp->login($DB_ftpuser, $DB_ftppass) or die "Cannot login ", $ftp->message;
     $ftp->get("/" . $root . $webpage, "c:/perlscripts/" . md5_hex($md5Con) . "-code.php") or die $ftp->message;
     open FILE, ">>c:/perlscripts/" . md5_hex($md5Con) . "-code.php" or die $!;
     $fileContents = <FILE>;
     close(FILE);
     unlink("c:/perlscripts/" . md5_hex($md5Con) . "-code.php");
     $ftp->quit;
 }

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

Но я не могу понять, как получить содержимое файла;

open FILE, ">>c:/perlscripts/" . md5_hex($md5Con) . "-code.php" or die $!;
$fileContents = <FILE>;
close(FILE);

продолжайте получать ошибки;

Использование неинициализированного значения $ fileContents

Что, я думаю, означает, что оно не возвращает значение.

Любая помощь высоко ценится.

>>>>>>>>> РЕДАКТИРОВАТЬ <<<<<<<<<<

my $fileContents;
if( $md5Con =~ m/\.php$/g ) {
    my $ftp = Net::FTP->new($DB_ftpserver, Debug => 0) or die "Cannot connect to some.host.name: $@";
    $ftp->login($DB_ftpuser, $DB_ftppass) or die "Cannot login ", $ftp->message;
    $ftp->get("/" . $root . $webpage, "c:/perlscripts/" . md5_hex($md5Con) . "-code.php") or die $ftp->message;
    my $file = "c:/perlscripts/" . md5_hex($md5Con) . "-code.php";
    {
        local( $/ ); # undefine the record seperator
        open FILE, "<", $file or die "Cannot open:$!\n";
        my $fileContents = <FILE>;
        #print $fileContents;
        my $bodyContents;
        my $headContents;

        if( $fileContents =~ m/<\s*body[^>]*>.*$/gi ) {
            print $0 . $1 . "\n";
            $bodyContents = $dbh->quote($1);    
        }
        if( $fileContents =~ m/^.*<\/head>/gi ) {
            print $0 . $1 . "\n";
            $headContents = $dbh->quote($1);    
        }

        $bodyTable = $dbh->quote($bodyTable);
        $headerTable = $dbh->quote($headerTable);
        $dbh->do($createBodyTable) or die " error: Couldn't create body table: " . DBI->errstr;
        $dbh->do($createHeadTable) or die " error: Couldn't create header table: " . DBI->errstr;
        $dbh->do("INSERT INTO $headerTable ( headData, headDataOutput ) VALUES ( $headContents, $headContents )") or die " error: Couldn't connect to database: " . DBI->errstr;
        $dbh->do("INSERT INTO $bodyTable ( bodyData, bodyDataOutput ) VALUES ( $bodyContents, $bodyContents )") or die " error: Couldn't connect to database: " . DBI->errstr;
        $dbh->do("INSERT INTO page_names (linkFromRoot, linkTrue, page_name, table_name, navigation, location) VALUES ( $linkFromRoot, $linkTrue, $page_name, $table_name, $navigation, $location )") or die " error: Couldn't connect to database: " . DBI->errstr;

        unlink("c:/perlscripts/" . md5_hex($md5Con) . "-code.php");
    }
    $ftp->quit;
}

вышеупомянутое использование print БУДЕТ напечатать весь файл. НО, по какой-то причине два регулярных выражения возвращают false. Есть идеи почему?

if( $fileContents =~ m/<\s*body[^>]*>.*$/gi ) {
            print $0 . $1 . "\n";
            $bodyContents = $dbh->quote($1);    
        }
        if( $fileContents =~ m/^.*<\/head>/gi ) {
            print $0 . $1 . "\n";
            $headContents = $dbh->quote($1);    
        }

7 ответов

Решение

Это описано в разделе 5 FAQ по Perl, включенному в стандартный дистрибутив.

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

Вы можете использовать Path::Class::File::slurp Модуль, чтобы сделать это за один шаг.

use Path::Class;
$all_of_it = file($filename)->slurp; # entire file in scalar
@all_lines = file($filename)->slurp; # 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_File модуля $DB_RECNO привязки, которые позволяют привязать массив к файлу так, чтобы при доступе к элементу массив фактически обращался к соответствующей строке в файле.

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

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

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

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

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

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

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

Используйте Path::Class::File::slurp, если вы хотите прочитать все содержимое файла за один раз.

Однако, что более важно, используйте анализатор HTML для анализа HTML.

open FILE, "c:/perlscripts" . md5_hex($md5Con) . "-code.php" or die $!;
while (<FILE>) {
    # each line is in $_
}
close(FILE);

откроет файл и позволит вам обрабатывать его построчно (если это то, что вы хотите - иначе исследуйте binmode). Я думаю, что проблема в том, что вы добавляете имя файла для открытия >>, Смотрите этот учебник для получения дополнительной информации.

Замечу, что вы также используете регулярные выражения для разбора HTML. Обычно я бы рекомендовал использовать для этого парсер (например, см. HTML:: Parser). Регулярные выражения не подходят для HTML из-за отсутствия регулярности в HTML и не будут работать надежно в общих случаях.

Также, если вам нужно отредактировать содержимое файлов, взгляните на модуль CPAN Tie::File
Этот модуль избавляет вас от необходимости создания временного файла для редактирования содержимого и записи его обратно в тот же файл.

РЕДАКТИРОВАТЬ:
То, на что вы смотрите - это способ украсть файл. Может быть, вам нужно отменить определение разделителя записей $/

Приведенный ниже код отлично работает для меня:

use strict;
my $file = "test.txt";
{
    local( $/ ); # undefine the record seperator
    open FILE, "<", $file or die "Cannot open:$!\n";
    my $lines =<FILE>;
    print $lines;
}

Также см. Раздел "Традиционное хлебание" в этой статье.

НО, по какой-то причине два регулярных выражения возвращают false. Есть идеи почему?

. в регулярном выражении по умолчанию соответствует любой символ, кроме новой строки. Предположительно у вас есть переводы строк перед </head> тег и после <body> тег. Делать . соответствовать любому символу, включая символы новой строки, используйте //s флаг.

Я не уверен, что ты print $0 . $1 ... код о; вы ничего не захватываете в своих совпадениях для хранения в $1, а $0 - это не переменная, используемая для захвата регулярных выражений, это что-то совсем другое.

Если вы хотите получить содержимое файла,

 @lines = <FILE>;

Использование File::Slurp::Tiny, Так же удобно, как File::Slurp, но без багов.

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