Perlでは、ファイル全体を文字列に読み込むにはどうすればよいですか?


118

.htmlファイルを1つの大きな長い文字列として開こうとしています。これは私が持っているものです:

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

これにより、ドキュメント全体をより簡単に検索できます。


8
「カントインストール」の定義が何であるかを確認する必要があります。これは一般的な問題であり、一般的には行う必要のない議論です。stackoverflow.com/questions/755168/perl-myths/...
ケントフレドリックに

1
実際には、このスクリプト自体を除いて、このスクリプトが実行されているサーバー全体を変更することはできません。
goddamnyouryan 09/06/05

つまり、サーバー上のどこにでもファイルを追加することはできませんか?
ブラッドギルバート、

FatPackモジュールをスクリプトに追加しますか?また、正規表現を使用してHTMLを解析することを考えているようですが、そうしないでください。
MkV 2013年

回答:


81

追加:

 local $/;

ファイルハンドルから読み取る前。参照してください。私はすべてが一度にファイル全体を読み込むことができますどのように?、または

$ perldoc -q "ファイル全体"

およびのファイルハンドル関連する変数を参照してください。perldoc perlvarperldoc -f local

ちなみに、スクリプトをサーバーに配置できれば、必要なすべてのモジュールを使用できます。参照してください。私は自分自身のモジュール/ライブラリディレクトリを維持するにはどうすればよいですか?

さらに、Path :: Class :: Fileを使用すると、丸呑み吐き出しを行うことができます。

パス::タイニーは、のような、より便利なメソッド与えslurpslurp_rawslurp_utf8並びにそれらのspew対応を。


33
$ /のローカライズがどのような効果をもたらすか、その目的は何かを説明する必要があります。
Danny

12
ローカライズについて何も説明しない場合$/は、詳細情報へのリンクを追加する必要があります。
ブラッドギルバート

7
何が行われているのかを段階的に説明した良い例:{local $ /; <$ fh>}はここにあります:perlmonks.org/
node_id=

たぶん、なぜあなたが使わなければならないのか、なぜ使わlocalないのかを言ってくださいmy
ジェレミア2016年

@Geremiaスコーピングの議論はこの回答の範囲を超えています。
SinanÜnür2016年

99

私はそれをこのようにします:

my $file = "index.html";
my $document = do {
    local $/ = undef;
    open my $fh, "<", $file
        or die "could not open $file: $!";
    <$fh>;
};

引数が3つのバージョンのopenの使用に注意してください。古い2つ(または1つ)の引数バージョンよりもはるかに安全です。字句ファイルハンドルの使用にも注意してください。字句ファイルハンドルは、多くの理由で、古いベアワードバリアントよりも優れています。ここではそのうちの1つを利用しています。範囲外になると閉じます。


9
これは、3つの引数を開いた状態で使用するだけでなく、INPUT_RECORD_SEPARATOR($ /)変数を必要な最小のコンテキストにローカライズしたままにするため、cpanを使用しない最良の方法です。
Danny

77

ファイル:: SLURP

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

はい、CPANを使用することもできます


OPは、サーバー上の何も変更できないと述べました。ここの「はい、CPANを使用することもできます」リンクは、ほとんどの場合、その制限を回避する方法を示しています。
Trenton

Can't locate File/Slurp.pm in @INC (@INC contains: /usr/lib/perl5/5.8/msys:(
Dmitry

2
@Dmitry —モジュールをインストールします。この回答からリンクしたmetacpanページにインストール手順のリンクがあります。
クエンティン

53

すべての投稿は少し慣用的ではありません。イディオムは次のとおりです。

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

ほとんどの場合、$ /をに設定する必要はありませんundef


3
local $foo = undefPerlのベストプラクティス(PBP)で提案されている方法です。コードのスニペットを投稿する場合、明確にするために最善を尽くすことは良いことだと思います。
Danny

2
非慣用的なコードを書く方法を人々に示すことは良いことですか?私が取り組んでいるコードで "local $ / = undef"を見つけた場合、最初のアクションは、著者をircで公に屈辱的にすることです。(そして、私は一般的に「スタイル」の問題にこだわりません。)
jrockway

1
わかりました、かみます:「ローカル$ / = undef」について正確にモックに値するものは何ですか?あなたの唯一の答えが「それは慣用的ではない」である場合、(a)わからない、(b)だから何ですか?これを行う方法としてひどくよくあるので、私はあまりわかりません。そして、それは完全に明確で合理的に簡潔だからです。あなたはあなたが考えるスタイルの問題についてもっとうるさいでしょう。
テレマコス

1
重要なのは、「ローカル$ /」がよく知られたイディオムの一部であることです。ランダムなコードを書いていて、 "local $ Foo :: Bar = undef;"と書けば問題ありません。しかし、この非常に特殊なケースでは、「それほど明確ではない」(私は同意しません。「ローカル」の動作はこの点で明確に定義されています)場合でも、他の人と同じ言語を話すのもよいでしょう。
jrockway 2009年

11
すみません、同意しません。マジック変数の実際の動作を変更したい場合は、明示的にする方がはるかに一般的です。それは意図の宣言です。ドキュメントでも「local $ / = undef」を使用しています(perldoc.perl.org/perlsub.html#Temporary-Values-via-local()を参照)
Leonardo Herrera

19

perlfaq5から:ファイル全体を一度に読み取るにはどうすればよいですか?


File :: Slurpモジュールを使用して、1つのステップでそれを行うことができます。

use File::Slurp;

$all_of_it = read_file($filename); # entire file in scalar
@all_lines = read_file($filename); # one line per element

ファイル内のすべての行を処理するための通常のPerlのアプローチは、一度に1行ずつ処理することです。

open (INPUT, $file)     || die "can't open $file: $!";
while (<INPUT>) {
    chomp;
    # do something with $_
    }
close(INPUT)            || die "can't close $file: $!";

これは、ファイル全体を行の配列としてメモリに読み込み、一度に1つの要素を処理するよりも、非常に効率的です。あなたが誰かがこれをするのを見たときはいつでも:

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

3番目の引数は、INPUTファイルハンドルのデータのバイトサイズをテストし、そのバイト数をバッファー$ varに読み取ります。


8

簡単な方法は次のとおりです。

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

もう1つの方法は、入力レコードセパレータ "$ /"を変更することです。グローバルレコードセパレーターの変更を回避するために、ベアブロックでローカルに行うことができます。

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

1
あなたが与えた例の両方にかなりの数の問題があります。主な問題は、私が読んで推薦する、それらは古代のPerlで書かれているということである現代Perlを
ブラッド・ギルバート

@Brad、コメントは数年前に行われましたが、要点はまだ残っています。良いのは{local $/; open(my $f, '<', 'filename'); $d = <$f>;}
ジョエルバーガー

@ジョエルは少しだけ良いです。openまたは暗黙的に呼び出されたの出力を確認していませんclosemy $d = do{ local $/; open(my $f, '<', 'filename') or die $!; my $tmp = <$f>; close $f or die $!; $tmp}。(それでも入力エンコードが指定されていないという問題があります。)
Brad Gilbert

use autodie、私が示すつもりの主な改善点は、字句ファイルハンドルと3つの引数のオープンでした。これを使っている理由はありますdoか?ブロックの前に宣言された変数にファイルをダンプしないのはなぜですか?
Joel Berger

7

に設定$/するかundef(jrockwayの回答を参照)、ファイルのすべての行を連結します。

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

それをサポートするすべてのバージョンのPerlでは、ファイルハンドルにスカラーを使用することをお勧めします。


4

別の可能な方法:

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

3

<FILE>スカラーコンテキストで評価しているので、ひし形演算子から最初の行のみを取得しています。

$document = <FILE>; 

リスト/配列のコンテキストでは、ひし形演算子はファイルのすべての行を返します。

@lines = <FILE>;
print @lines;

1
命名法についてのメモ:宇宙船オペレーターはで<=>あり、<>はダイヤモンドオペレーターです。
ツール

ああ、ありがとう、私はこれまで「ダイヤモンドオペレーター」と聞いたことはなく、両方とも同じ名前を共有していると思っていました。上記で修正します。
ネイサン

2

私は最も簡単な方法でそれを行うので、よりスマートな方法があっても、誰でも何が起こるかを理解できます。

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

これらの文字列の連結はすべて非常にコストがかかります。私はこれを避けるでしょう。なぜ元に戻すためだけにデータを切り離すのですか?
andru

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

<f>-ファイルから行の配列を返し($/デフォルト値がある場合"\n")、join ''この配列を追加します。


2

これは、より多くの方法についての提案である、NOTそれを行います。かなり大きなPerlアプリケーションのバグを見つけるのに苦労しました。ほとんどのモジュールには独自の構成ファイルがありました。設定ファイル全体を読むために、インターネット上のどこかにこのPerlの1行を見つけました。

# Bad! Don't do that!
my $content = do{local(@ARGV,$/)=$filename;<>};

前に説明したように、行区切りを再割り当てします。ただし、STDINも再割り当てされます。

これには少なくとも1つの副作用があり、見つけるのに何時間もかかりました。暗黙のファイルハンドルが適切に閉じられません(まったく呼び出さ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

奇妙なことに、$.ファイルごとに行カウンターが1つずつ増加します。リセットされず、行数も含まれません。そして、少なくとも1行が読み込まれるまで、別のファイルを開いたときにゼロにリセットされません。私の場合、私は次のようなことをしていました:

while($. < $skipLines) {<FILE>};

この問題のため、ラインカウンターが正しくリセットされなかったため、条件はfalseでした。これがバグなのか、単に間違ったコードなのかはわかりません... close;oderを呼び出しclose STDIN;ても役に立ちません。

この判読できないコードは、open、string連結、closeを使用して置き換えました。ただし、代わりに明示的なファイルハンドルを使用するため、Brad Gilbertによって投稿されたソリューションも機能します。

最初の3行は次のように置き換えることができます。

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};

これにより、ファイルハンドルが適切に閉じます。


2

使用する

 $/ = undef;

以前$document = <FILE>;$/ある入力レコードセパレータ、デフォルトで改行され、。これをに再定義するとundef、フィールドセパレータがないことになります。これは「スラップ」モードと呼ばれます。

undef $/およびlocal $/(ではないがmy $/)のような他のソリューションは$ /を再宣言するため、同じ効果が得られます。



0

それが良い習慣かどうかはわかりませんが、以前はこれを使用していました:

($a=<F>);

-1

これらはすべて良い答えです。しかし、怠惰でファイルがそれほど大きくなく、セキュリティが問題ではない場合(汚染されたファイル名がないことがわかっている場合)、シェルアウトできます。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.