「初めてのPerl」 8章 (正規表現の詳細)

講習会(研修のようなもの)に参加するために8月8日から東京に行くのですが、なんだか会社の人たちからは早めに夏休みを取っているように思われているbonlifeです。納得が行きません…。さてさて、「初めてのPerl」の8章は7章に引き続き正規表現についての説明です。

  • 文字クラス (ブラケット[]の間に文字を並べたもの)
  • 文字クラスのショートカット (「ワード」は\w、空白文字は\sなど)
  • ショートカットの否定 (非数字は[^\d]など)
  • 汎用の量指定子 ({3,5}で3回以上5回以下の繰り返し)
  • アンカー (文字列の先頭を表す^や末尾を表す$)
  • ワードアンカー (\b単語\b)
  • カッコによる記憶、後方参照
  • 優先順位

などなど結構盛り沢山です。正規表現素人でもこの章の内容をマスターすれば結構な正規表現使いになれそうです。私の場合、体系的に勉強していなかったのでワードアンカー(\b)を知りませんでしたよ…。また、後方参照時、左カッコから順に1から数字が割り当てられる、というのは勉強になりました。そんなこんなで、8章の演習問題のnot模範解答です。
ex8-1.pl

  • ワードfredまたはwilmaがあり、次に何個かの空白文字があり、その後ろにワードflintstoneがあるような行にだけマッチするパターンを作成し、テスト
#! perl -w
use strict;
while (<>) {
	chomp;
	if (/([fF]red|[wW]ilma)\s+flintstone/) {
		print "Matched : | $`<$&>$' |\n";
	} else {
		print "No match.\n";
	}
}

出力結果は以下の通り。

ex8-1.pl
Please input some words :
I am fred flintstone
Matched : | I am <fred flintstone> |
You are wilma     flintstone
Matched : | You are <wilma     flintstone> |
He is afred flintstone
Matched : | He is a<fred flintstone> |

解答例を見て気付いたことは以下の通り。

  • 完全にワードとして扱うのを忘れておりました (パターンの先頭、末尾に\bが必要)

ex8-2

  1. /"([^"]*)"/
  2. /^0?[0-3]?[0-7]{1,2}$/
  3. /^\b[\w.]{1,12}\b$/

解答は以下の通り。

  1. ダブルクォートの間にダブルクォートではない文字が0回以上現れるもの (HTMLタグの属性値など)
    つまり、ダブルクォートに囲まれた文字列
  2. 0回、または1回の0の後、0〜3が0回、または1回出現し、その後、0-7が1回から2回繰り返されて終わるもの
    8進数っぽい
  3. スペースに区切られた、数字またはアルファベット1文字の後、任意の1文字の1回から12回の繰り返し
    1〜12文字の文字列 (.あり)

解答例を見て気付いたことは以下の通り。

  • 2は勘が当たっていてビックリ
  • 3は\bで挟まれているので文字列の先頭と末尾にはドットを置けない

ex8-3.pl

  • スカラー変数名だけを含むような文字列にマッチするパターンを作成
#! perl -w
use strict;
print "Please input some words : \n";
while (<>) {
	chomp;
	if (/^\$[A-Za-z_]\w*$/) {
		print "Matched : | $`<$&>$' |\n";
	} else {
		print "No match.\n";
	}
}

出力結果は以下の通り。

ex8-3.pl
Please input some words :
$wilma
Matched : | <$wilma> |
wilma
No match.
$0
No match.
^Z

解答例を見て気づいたことは特になし。同じ。とか言いつつ、最初、2文字目以降は数字もO.K.だということを忘れておりました。いくつか文字列を入力してる時に気づいて慌てて修正です。(慌てる必要なんて全くないのですが。)
ex8-4.pl

  • 同じワードが連続して2回以上出現する行にマッチするパターンを作成
#! perl -w
use strict;
print "Please input some words : \n";
while (<>) {
	chomp;
	if (/\b(\w+)\b(\s+\1)+\b/) {
		print "Matched : | $`<$&>$' |\n";
	} else {
		print "No match.\n";
	}
}

出力結果は以下の通り。

ex8-4.pl
Please input some words :
Paris in the the spring
Matched : | Paris in <the the> spring |
I think that that is the problem
Matched : | I think <that that> is the problem |
I think that that   that is the problem
Matched : | I think <that that   that> is the problem |
This is a test
No match.
This shouldn't match, according to the theory of regular expressions
No match.

解答例を見て気づいたことは以下の通り。

  • 2個目の\bは不要 (\w+が単語にマッチする)

最初、3回以上の繰り返しの対応が考慮できておらず、何度か修正しましたよ。このレベルになってくるとさすがにサラッと書けないですね…。正規表現の章ではないところでこの問題が出ていたら、1行ずつ取り込んでスペース区切りで分解して配列に格納し、値を1語ずつ比較するようなスクリプト書いてただろうなぁ、とか思ったりしました。正規表現ってやっぱり便利です。そろそろ細々とした間違いも出てきましたので、次の章からはもう少し慎重に演習問題を解いていこうと思います!