2011-12-22 18 views
0

Jeffrey Friedlの優れたMastering Regular Expressionsを読んでPerlとRegexを教えています。テキストファイルの内容をPerlのオフセットから末尾まで読む

53ページから始まる「小さなメールユーティリティ」の練習問題を解決しようとしたが、ファイルの内容をオフセットから始まる変数に保存する方法がわからないという問題に直面した。

私の(短縮版)スクリプトです。

my ($body, $line, $subject); 
$body = $line = $subject = ""; 

open(MYFILE, "king.in") || die("Could not open file!");  
# Read the file's content line by line 
while ($line = <MYFILE>) 
{ 
    # An empty line marks the beginning of the body 
    if ($line =~ m/^\s+$/) { 
     # HERE IS THE ISSUE 
     # Save the file content starting from the current line 
     # to the end of the file into $body 
     last; 
    } 

    if ($line =~ m/^subject: (.*)/i) { 
     $subject = $1; 
    } 
    # Parse additional data from the mail header 
} 
close(MYFILE); 

print "Subject: Re: $subject\n"; 
print "\n" ; 
print $body; 

私はいくつかのオンライン調査をしましたが、変数$本体にファイル(つまり、電子メールの本文)の残りの部分を配置する方法を見つけ出すことができませんでした。

私は最終的に、私は配列に最初のファイルの行を入れての作業が、不十分なsolutionになってしまった私は$pos = tell(MYFILE);

を使用して、バイト単位でファイル内の現在位置を取得することができることを考え出しました。

$ bodyにオフセット(行番号またはバイトのいずれか)からファイル内容を保存するにはどうすればよいですか?

編集: 私のソリューションの-as vstm-によって提供さは、本体の始まりを示す空行に遭遇したとき、ファイルの残りの部分を読み込むこと$body = join("", <MYFILE>)を使用することです。 私が書いたスクリプト全体はhereです。

これは私にはうってつけですが、Perlで "このファイルのx〜z行を教えてください"という言い方をしたいと思います。

皆様のご助言ありがとうございます。

答えて

1

ただちに停止する代わりに、「今は身体を読んでいます」というフラグを設定することができます。このように:

my $inbody = 0; 

while ($line = <MYFILE>) 
{ 
    if($inbody) { 
     $body .= $line; 
     next; 
    } 
    # An empty line marks the beginning of the body 
    if ($line =~ m/^\s+$/) { 
     # HERE IS THE ISSUE 
     # Save the file content starting from the current line 
     # to the end of the file into $body 
     $inbody = 1; 
     next; 
    } 

    if ($line =~ m/^subject: (.*)/i) { 
     $subject = $1; 
    } 
    # Parse additional data from the mail header 
} 

ミニステートマシンのようです。最初は "ヘッダ"状態にあり、最初の空白の改行が読み込まれた場合、それは "body"状態に切り替えられ、その本体を変数に追加するだけです。

代わりにあなたは自分の元while - ループの終わりとclose前に体内にMYFILE -handleの残りを読まできます

# This would be your original while loop, (I've just shortened it) 
while ($line = <MYFILE>) 
{ 
    if ($line =~ m/^\s+$/) { 
     last; 
    } 
    # Parse additional data from the mail header 
} 

# The MYFILE-handle is now still valid and at the beginning of the body 
$body = join("", <MYFILE>); 

# now you can close the handle 
close(MYFILE); 
+0

'$ body = join(" "、);'トリックを完璧にやってくれました。好奇心から:どのように私は "このファイルのxからzまでの行をPerlで教えてください"と言いますか?最初にすべての行を配列に入れる必要がありますか? –

0

あなたが入力レコードセパレータを変更することができます。

local $/; 
$body = <MYFILE>; 
2

変数$.は、現在のファイルハンドルの行番号を示します。ドキュメントhere.

ファイル内のオフセットをバイト単位で取得する場合は、seekを使用してファイルハンドルの位置を設定できます。しかし、通常、バイトは実際には希望のオフセットでない限り、実際にはそうしたくありません。

最も簡単な解決法はおそらく入力レコードセパレータを使用することです。undefに設定すると、代わりに、行ごとにそれを読んで、ファイルを読まれます

my $text; 
my $subject; 
while (<MYFILE>) { 
    if (/^subject: /i) { # /i flag to ignore case 
     $subject = $_; 
    } elsif (/^\s*$/) { 
     local $/; 
     $text = <MYFILE>; 
    } 
} 

それがEOFに達しているので、これは、同様のループを終了します。

+0

ありがとう、入力レコードの区切りをundefに設定することは素晴らしいことです。 'join(" "、)'と比べてパフォーマンスの違いはありますか? –

+0

@mareserローカルコピーを作成することは、それをundefに設定する方法です。どのソリューションが優れたパフォーマンスを発揮しているのかわかりません。重要な場合は、いつでもベンチマークすることができます。 CPANでベンチマークモジュールを検索します。 – TLP

+0

ああ、そうです。私は '$ /'(私はPythonの時間をいくつかやった)よりもよく見えるので、join()メソッドを好む。それ以外にも、「このファイルのx〜z行を教えてください」と言ってもいいか? –

関連する問題