Buho No.218 目次

Perl講座 第3回

こんの


今回は,主に正規表現を取り扱うことにします。

正規表現とは

最初に正規表現の厳密な定義を書こうと思ったのですが, 非常に難解なため僕にも理解できないので, おおざっぱに説明します 1 2

いきなり「正規表現」という言葉を見ても 何をどう表現しているのか 意味が分かりにくいと思いますが, 簡単な例をあげて説明してみます。 例えば .public_htmlディレクトリ下に移動して

% ls

とすると,ファイル名のリストが得られます。 ファイル名の拡張子も様々だと思いますが,

% ls *.html

とすると,\texttt{html}という拡張子が ついたファイルのみのリストが得られたと 思います。(存在するのであれば) \texttt{cgi}という 拡張子がついたファイルはリストから除かれているはずです。 この処理では,リスト中のファイル名のうち, 「任意の文字列 + ピリオド + html」という パターンに一致するものだけを選んで表示しています。 つまり,

「○○.html」,「△△.html」,「□□.html」, ......

というふうに,いちいち羅列して指定しなくても 最後に.htmlがつくファイル名のみが 自動的に抽出されたわけです。

これは後で出てくる正規表現から見ると 例としては相当悪いかもしれませんが, 正規表現とはこのように, ある規則に基づいた,文字列のパターンの表現です。 「Aが最初に来て,1個以上3個以下のlが 続き,0個以上の文字が続いた後にwで終わる」と いった正規表現も可能です 3 例えば /usr/dict/wordsをこの正規表現にかけてみましょう。

% perl -ne 'print if /^al{1,3}\w*w$/' /usr/dict/words
allow

今はこれの意味が分からなくても構いませんが, やっている内容は「/usr/dict/wordsという英単語がつまっている ファイルの中から,あるパターン(さっきのパターン)に一致するものを 検索して表示する」といったものです。 確かに「allow」は一致していますね。

正規表現のメタキャラクタ

正規表現のメタキャラクタはその文字そのものを 表わすのではありません。 例えば,この前出てきた「\n」は, 実は改行を表わすメタキャラクタです。 もしも「\n」という文字そのものを表わすのならば, 直後の1文字の特別な意味をなくすメタキャラクタ 「\」を用いて,「\\n」と書くことになります 4

それでは,主なメタキャラクタを掲げてみましょう。

メタキャラクタ(例)一致するもの
単一の文字や数字にマッチするもの
.改行文字を除く任意の1文字
\d1つの数字
\wアルファベットまたは数字(単語)の1文字
空白文字
\s空白文字(スペース,タブ,改行)
\n改行
\r復帰
\tタブ
\eエスケープ文字
位置指定(アンカー)
^文字列の先頭
$文字列の末尾
繰り返しパターン
x?0個または1個のx
x*0個以上のx
x+1個以上のx
x{n,m}n個以上m個以下のx
x{n,}n個以上のx
x{n}n個のx
abc文字列中のabcというパターンにマッチ

実際にはここにあるもの以外にも 正規表現のメタキャラクタは存在するのですが, 機会があった時にでも追い追い説明していくことにします。

検索演算子

今回は正規表現によるパターン検索を行うので, 検索用の演算子のみ, ここで先に簡単に取り上げることにします。 その他の置換等の演算子は今度詳しくやります。

あるパターンPATTERNに 一致するかどうかを検索するには,

/PATTERN/

という検索演算子を用います。 この検索演算子とともに,スカラー $word について, パターンに一致することを示す演算子 =~ と 典型的なif文を用いれば,

if($word =~ /PATTERN/){...}
PATTERNに一致すれば 以下を実行する)といった構文が可能です。 また,逆にパターンに一致しないことを示す 否定の意の演算子 !~というのも存在し,
if($word !~ /PATTERN/){...}
PATTERNに一致しなければ 以下を実行する)といった構文も可能です。

実例: ホームページのカウンター(1)

ホームページに設置するカウンターには 様々なものがありますが, ここではとりあえず, ファイルから現在の来訪者を既に スカラー変数 $countに 読み込んであって, その値を表示させることにしましょう。 ただし,ただ数字を表示するだけでは面白くないので, 正規表現を用いて,人数の値に応じてthやstなどを 数字の後ろにつけることにしましょう。 検索の流れは以下の通りです。

  1. 下2桁が「11」ならば「--th」となる。
  2. 下2桁が「12」ならば「--th」となる。
  3. 下2桁が「13」ならば「--th」となる。
  4. 下1桁が「1」ならば「--st」となる。
  5. 下1桁が「2」ならば「--nd」となる。
  6. 下1桁が「3」ならば「--rd」となる。
  7. 以上のいずれにも入らないならば「--th」となる。

ここでは,

if(式){ブロック}elsif(式){ブロック}...else{ブロック}

という条件分岐を用いることにします。 ちょっと elsifという文字に面食らいますが, まず if節が評価され, 次に elsif節が評価されていき, 最終的に残ったものが else節で評価されるという 流れになっています。

すると,最初に if節中で $countの 下2桁が「11」であるかどうかを 正規表現で検索することになります。 今までの知識を使うと, 文字列の末尾に「11」が並べばそれだけでいいわけですから, 「11$」となります。 つまり,

if($count =~ /11$/){...}

というわけですね 5

したがって,全ての検索を行って, 画面に表示させるまでのスクリプトは次のようになります。 ちなみに,スクリプト中で「#」以降は 行末までがコメントとみなされ,実行時には一切無視されます。

# $countには来訪者数の値が入っている。
if($count =~ /11$/){     # 下2桁が「11」ならば「-th」となる。
    print "$count th\n";
}elsif($count =~ /12$/){ # 下2桁が「12」ならば「-th」となる。
    print "$count th\n";
}elsif($count =~ /13$/){ # 下2桁が「13」ならば「-th」となる。
    print "$count th\n";
}elsif($count =~ /1$/){  # 下1桁が「1」ならば「-st」となる。
    print "$count st\n";
}elsif($count =~ /2$/){  # 下1桁が「2」ならば「-nd」となる。
    print "$count nd\n";
}elsif($count =~ /3$/){  # 下1桁が「3」ならば「-rd」となる。
    print "$count rd\n";
}else{                   # 以上のいずれにも入らないならば「-th」となる。
    print "$count th\n";
}

ただ,これだと出力が「1 st」のように なってしまって見苦しいので, 次回以降にその辺も含めて このスクリプトを改良したいと思います。

おまけ

やっぱり「Perlリソースキット --UNIX版」高杉。


1) 詳しく知りたい人は,技術評論社「The UNIX Super Text【上】」の25.5節などを読んでみるとよいでしょう。
2) 【編註】情報科学科の人をつかまえると,「形式言語理論」のテキストを見せてもらえることでしょう。
3) 「^al{1,3}\w*w\$」かな。
4) ここでは \\n での間に意味上の区切りがあることに注意しましょう。
5) なお,Perlでは if節等のブロック中の文が1つしかなくても,Cのようにブレース「{}」は省略できないので注意して下さい。

今野 俊一 (こんの, knn) <toknn@ijk.com>, <knn@ebony.plala.or.jp>
東京大学 工学部 計数工学科(内定), TSG(理論科学グループ)