Perlについてのちょっとした知識。
ループ内のI/O演算子(<>)の自動definedチェック †
while
やfor
ループでI/O演算子を使って行単位でデータを読み込むお馴染みの処理がある。while(my $line = <STDIN>) { ... }
- こういった場合、最近のPerlではループ条件式を自動的に
defined
でチェックするらしい。つまり上記の処理はwhile(defined(my $line = <STDIN>)) { ... }
と等価になる。 defined
でチェックしないとどうなるかというと、例えばファイル終端に改行なしの"0"が書いてあった場合、 これがfalse
とみなされてループを脱出してしまう。結果、"0"は処理されない。- 改行ありの"0\n"は
true
とみなされるので問題ない。 - ちなみに、
IO::Handle
オブジェクトなんかで同じ処理を書いた場合は自動チェックは適用されない。my $io = IO::Handle->new_from_fd(fileno(STDIN), "r"); while(my $line = $io->getline) { ... }
- 参考: http://perldoc.perl.org/perlop.html#I/O-Operators
サブルーチンの入れ子 †
chompと$/ †
chomp関数は文字列末尾に$/特殊変数で示される文字列がある場合にそれを取り除く。 $/にexact matchじゃないと除去してくれないので注意。
よって、Windows環境で作ったファイル(改行コード: CR + LF)の各行をLinux環境(改行コード: LF)のPerlでchompすると、CRが残ってしまう。
文字列末尾の改行やスペースを取り除くなら下のやり方の方が安全か。
$line =~ s/\s+$//;
一つの正規表現パターンで複数回マッチ †
一つの正規表現パターンに複数回マッチすることが見込まれ、 マッチした全ての部分文字列を取り出したい場合、以下のようにする。
@matchlist = ($testee =~ /パターン/g);
パターンの中に()で囲まれた部分パターンを入れておくと、 matchlist変数にそのパターンへマッチしたtestee変数内の部分文字列のリストが格納される。 パターンマッチ演算子にgオプションをつけておくのがミソ。 パターンには複数の()が含まれていてもいい。
1行if, 1行while †
Perlではif
節などの括弧{}
を省略することができない。
つまり、
# if(!&check($hoge)) die "Error!"; ← エラー if(!&check($hoge)) { die "Error!" }
てな感じなのである。
ただし、if
を1行で済ます文法もじつはちゃんとある。下のように書く。
die "Error!" if !&check($hoge);
こんなふうに、実行文の後ろにif 式
をくっつける。
このような文の後ろにくっつく条件構文を文修飾子(statement modifier)というらしい。
statement modifierには以下の4つがある。(EXPR
は式、LIST
はリストを表す)
if EXPR
unless EXPR
while EXPR
until EXPR
foreach LIST
機能はなんとなく分かる。
foreach
を使った場合、その文でLIST
から取り出された要素にアクセスするには$_
を使えばよい。
eachを使った繰り返しの注意点 †
while(($key ,$value) = each(%hash)) { .... }
上のコードは連想配列の全要素を走査する常套手段だが、
このループをlast
やreturn
で強制脱出すると大変なことが起きる。
どうも、each
でどのレコードを引っ張ってくるかはどこかに記録されているようで、each
を中途半端なところでやめた場合、次にeach
を呼び出すとその次からレコードを取り出すようになる。
それだけならそれほど問題でもないが、each
を途中でやめて、その後またeach
でレコードを取り出し続けると、なぜか取り出せないレコードが出てくるようだ。つまり、まだ取り出していないレコードがあるにもかかわらずeach
のループが終了してしまう。
よって、each
を使うときは必ず全てのレコードを走査させなければいけない。
eachとforeachの機能 †
foreach $elem (@array)
で配列中の各要素を参照できるが、これはC++言語風に書くと以下のようになる。
Type array[N]; int i; for(i = 0 ; i < N ; i++) { Type &elem = array[i]; ... }
つまり、$elem
はarray
の要素のコピーではなく、そのエイリアスといえる。
言い換えると、foreach
のブロック内部で$elem
に対して上書き処理をすると、@array
の対応する要素がちゃんと書き換わるということである。
一方、each
関数はハッシュの要素対の値を返すのであって、エイリアスを返すのではない。
つまり、以下のようなコードを書いてもハッシュの値を変更することはできない。
while(($key ,$value) = %hash) { $value += 50; }
ハッシュの値を変更したければこのように書かなければいけない。
while(($key ,$value) = %hash) { $hash{$key} += 50; }
requireの探索ディレクトリ †
スクリプト中では@INC
という配列にrequire
の探索元ディレクトリが格納されている。
コマンドラインからPerlを実行する時に、
perl -I [ディレクトリ] [スクリプトファイル名]
として実行すると、@INC
に-I
オプションで指定したディレクトリを追加できる。
また、環境変数PERLLIBにディレクトリ名を書き込んでおくとそのディレクトリはデフォルトで@INC
に追加される。
環境変数を書き出すには……まあ、OSによってもいろいろ方法が違う。
正規表現の"." †
"."は任意の文字にマッチするメタ文字だが、改行文字にはマッチしてくれない。 マッチ修飾子"s"を使うと改行文字にもマッチするようになる。
例:
$str =~ s/<p>(.*?)<\/p>/$1/gs;