スクリプトの実行開始時や終了時に(そしてその時のみに)処理を行う場合には,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つの方法が考えられる。