スクリプトの実行開始時や終了時に(そしてその時のみに)処理を行う場合には,BEGIN, END を使って指定する。
| BEGIN { 処理 } | Perl がスクリプトが読み込むとき「処理」が実行される。オプション -n, -p を付けて実行されるスクリプトの場合でも,「処理」が実行されるのは一度だけ。 |
| END { 処理 } | Perl が終了する直前に一度だけ「処理」が実行される。オプション -n, -p を付けて実行されるスクリプトの場合でも,「処理」が実行されるのは一度だけ。 |
while ( 条件 ) { 処理 } または 処理 while ( 条件 )
「条件」が成立している間,「処理」を行う。(詳細)
while ($var < 20) { $var = $var + 1; print "$var\n"; }
| 注意: | 「条件」部分で,比較演算子「==」と代入演算子「=」を書き間違えると無限ループに陥るので注意すること。「変数 $var の値が 20 である」のつもりで「$var = 20」と書いてしまうことは少なくないので気をつけた方がよい。無限ループに入ってしまったら,強制終了(cntrl + c)する。 |
while の条件部分にオプション g 付きの正規表現を書くと,該当する文字列が存在するだけマッチングを行うので,問題の文字列の出現回数を数えることができる。(if の場合との違いに注意。)
while (/\band\b/g) { $count++; }
以下のスクリプトを実行して,結果を確認しなさい。
1. % perl -e 'while ($var < 20){ $var = $var + 1; print "$var\n"; }'
2. % perl -ne 'while (/\band\b/g) {$count++;} END {print "$count\n";}' dll.txt
ファイルからデータを読み込むには,ダイヤモンド演算子 <> を使う。オプション -n, -p を付けた場合と同じように,ファイル中のすべてのレコードを一つづつ読み込むには,while と組み合わせ,以下のようにする。(< と > の間にスペースを入れないこと。)
while (<>) { 処理 }
while の条件部分で <> を使うと,読み込んだレコードは自動的に $_ に代入される。
grep のようにパターンにマッチする行だけ抜き出すには,if と組み合わせて次のようにすればよい。
while (<>) { if (/正規表現/) { print; } }
次のようにすれば,全ての行に対して同じ置換処理を行った結果が出力される。
while (<>) { s/文字列/置換文字列/g; print; }
特殊変数 $/ の値を変えることで,読み込むレコードの単位を変更することができる。$/ のデフォルトの値は "\n"。$/ の値を空の文字列 ("") とすると,空行をレコードの区切り文字 (record separator, RS) としてデータを読み込むことができる。
#worth を含む「段落」を出力する
% perl -e '$/ = ""; while (<>) {print if (/\bworth\b/i)}' dll.txt
$/ の値を未定義 (undef) にすると,ファイル全体が1レコードとなる。複数のファイルを指定すると,ファイル単位の処理をすることになる。
% perl -e '$/ = undef; while (<>) {print if (/\bRussia/i)}' voa/* > result.txt
while ループに入る前にレコードの区切り文字を指定する点に注意する。
オプション -n, -p を使い自動でレコードを読み込ませるようにした場合でも,BEGIN を使えば $/ の単位を自由に変えられる。(注意)
#ディレクトリ voa にあるファイルから 'Russia' を含むものを出力
% perl -ne 'BEGIN {$/ = undef;} print if (/\bRussia/i);' voa/*
#'Russia' を含むファイルの名前を出力
% perl -ne 'BEGIN {$/ = undef;} print "$ARGV\n" if (/\bRussia/i);' voa/*
dll.txt から worth を含む「段落」を抜き出しなさい。
スクリプトの内容をファイルに書き,そのファイルを指定してスクリプトを実行することができる。スクリプトファイルには拡張子 .pl を付けることが多い。
perl スクリプトファイル [入力ファイル] [> 出力ファイル]
スクリプトをテキストファイルとして保存すればよい。
% perl script/worth1.pl dll.txt
script/worth1.pl
-------------------------------------------------------
$/ = ""; while(<>){ if (/\bworth\b/i) { print; } }
-------------------------------------------------------
スペース,タブ,改行などは実行の際,無視されるので,見やすいように,スペースや改行などでレイアウトしてよい。また,# から改行までの部分は,実行の際,無視されるので,コメントなどを書き加える時に利用するとよい。
スクリプトは最後の処理が実行されると自動的に終了するが,明示的にスクリプトの終了を示すには「exit」を書く。
% perl script/worth2.pl dll.txt
script/worth2.pl
--------------------------------------------------
#レコード単位の指定
$/ = "";
#各レコードの処理
while(<>){
if (/\bworth\b/i) {
print;
}
}
exit;
--------------------------------------------------
% perl script/catlines.pl dll.txt | more
script/catlines.pl
--------------------------------------------------
#「段落」中の改行を取るスクリプト
$/ = ""; #空行をRSとする。
while (<>) { #「段落」単位で読み込んで,
s/ *\n */ /g; #改行を取り,
s/^ +//; #文字列先頭の不要なスペースを削除し,
s/ +$//; #文字列末の 〃 ,
print "$_\n\n"; #空行付きで出力する。
}
exit;
--------------------------------------------------
[複数行からなる文字列を指定する方法 (ヒアドキュメント)]
[スクリプトファイルを実行可能ファイルにする方法]
[スクリプトのチェックの仕方]
worth1.pl, catlines.pl の内容を cat で表示し確認した後,dll.txt を対象にコマンドラインから実行しなさい。
exit を使えば,ある条件が成立したらスクリプトを終了することができる。
if (条件) { exit; } または exit if (条件);
次の例のようにすれば,全てのレコードを処理せずにスクリプトを終了することができる。
--------------------------------------------------
#1〜20行を出力する
while (<>) {
exit if ($. == 21);
print;
}
--------------------------------------------------
next を while の処理のブロックの中で使うと,スクリプトは終了せずに,next 以下の処理は行わず,続けて while の条件をチェックする。
if (条件) { next; } または next if (条件);
次のスクリプトを実行すると,レコードが # で始まる場合,print を実行せずに次のレコードを読み込む。その結果,「# で始まる行を削除」したことになる。
--------------------------------------------------
while (<>) {
next if (/^#/);
print;
}
--------------------------------------------------
last を使えば,スクリプト自体は終了せずに,while のループを終了することができる。
while │ ┌─ 偽 <条件のチェック>←─┐ ス │ 真 │ ク │ ↓ │ リ←───── exit next ───→│ プ │ last │ ト │ │ │ │ の │ ↓ └───────┘ 終 └──────┐ 了 │ ↓ [次の処理]
次の変数を使うと,最後に正規表現にマッチした文字列の,様々な部分にアクセスすることができる。
| $& | 直前で正規表現にマッチした文字列全体。
% perl -pe 's/Japan[a-z]*/__$&__/ig;' voa/2-257714.txt % perl -pe 's/\bnot\W+to\b/<<$&>>/ig;' dll.txt |
| $n | 正規表現に含まれる,n番目の ( ) で囲まれた部分にマッチした文字列。
s/\b(after|before) (\S+ing)\b/<<$1>> [[$2]]/ig; #コンマ区切りデータの第1・第2フィールドを入れ換える (ファイルの内容) % perl -pe 's/^([^,]*),([^,]*)/$2,$1/' data/meibo1.txt [$1, $2, ... の値を一度に変数に代入する方法] |
| $+ | 最後にマッチした正規表現の,一番右の ( ) にマッチした文字列。
#行末の10文字を取りだす
% perl -ne '/(.{0,10})$/; print "$+\n"' dll.txt
[その他の例]
|
| $` | 正規表現にマッチした部分の前の文字列。 |
| $' | 正規表現にマッチした部分の後ろの文字列。
--------------------------------------------------
$/ = "";
while(<>){
s/ *\n */ /g;
s/ +$//;
while(/\b(am|are|is)(n't| not)? [^ ]+ing\b/ig){
print "$`\t$&\t$'\n";
}
}
--------------------------------------------------
|
| 注意: | 上のスクリプトの while の条件にあるオプション g を書き忘れると,無限ループに入るので注意すること。 |
言語研究では,テキストが1文1行に整形されていると便利なことが多い。これまでに学んできたことを利用すれば,かなりのところまで,自動で整形するスクリプトを書くことができる。([正規表現])
% perl script/ll2ss.pl dll.txt | more
script/ll2ss.pl
--------------------------------------------------
$/ = "";
while (<>) {
#処理対象以外の行は単に出力するだけ (注)
if (/^#/) {
print;
next;
}
#段落中の行をつなぐ
s/ *\n */ /g;
#段落最後に挿入された余計なスペースを取る
s/ +$//;
#文の切れ目らしきところに改行を入れる
s/([.?!]\W*) +(\W*[A-Z])/$1\n$2/g;
#余分に改行を入れてしまったところを戻す
s/(Mr|Mrs)\. *\n/$1\. /g;
#空行付きで出力する
print $_,"\n\n";
}
exit;
--------------------------------------------------
注: 処理対象外の「段落」の先頭には # が付いているものと仮定している。
ヒント1:話と話の間に空行2つが入っていることに着目する。
ヒント2: 「うさぎに当たる英単語 (rabbit, hare)」と「亀に当たる英単語 (tortoise, turtle)」が両方出現しているデータを抜き出すには,次の2つの方法が考えられる。