電話帳や郵便番号簿から目的のデータを探すときはどうするだろう。 おそらく先頭から1、2文字(イニシャル)を利用して、検索ボタンなり索引を使っ て探すだろう。誰が登録されているか覚えていればよいが、未知のものから探す のはたいへんである。また、データの先頭の文字を忘れていたり、 「サトウ」なのか「サイトウ」なのかとか、 「イカラシ」なのか「イガラシ」なのかなど、途中があやふやだったりすると 検索機能では探せない。
なにかを検索するときに、そのデータに含まれている文字列のパターンを 汎用的に指定する方法がある。そのうち、もっとも一般的であるものが 正規表現である。
たとえば、「サトウ」と「サイトウ」どちらかを探したい場合、 正規表現では次のように検索パターンを書く。
サイ?トウ
こうすると、"サトウ" または "サイトウ" 両方にマッチするようになる。
特別な文字 ?
を指定すると、「その直前の文字があってもなくて
もよい」という特別な意味になる。また、別の特別な文字 [ ]
を
使っても、特別な意味を持った指定ができる。たとえば、
イ[カガ]ラシ
とすると、"イカラシ" でも "イガラシ" でもマッチするようになる。
特別な文字 [ ]
の中に何文字か文字を列挙すると、
「その中のどれかの文字が来ればよい」という指定になる。
egrep
コマンドによる正規表現の実験実際に名簿のようなデータを作って、正規表現で検索する練習をしてみよう。
最初に検索のデータとなるファイルを作っておこう。以下のような名簿データファ
イル、meibo.txt
を作成しよう。
サトウ ムネユキ SATOH Muneyuki さとう胸幸 青葉台 ナカマチ タロウ NAKAMACHI Taro 中町太郎 酒田 イイモリ ハナコ IIMORI Hanako 飯森花子 飯森山 サイトウ テツヤ SAITOH Tetsuya 斎藤徹夜 白夜の国 ゲゲノ キタロウ GEGENO Kitarou 下々野喜太郎 魔界村 ナニワノ タロー NANIWANO Taro 浪花之太朗 探偵騎士王国 ヒミ ヨンタロウ HIMI Yontarou 悲見四太郎 甲子園 ヤマタイ ヒミコ YAMATAI Himiko 邪馬台氷見子 大和 イモリ テツコ IMORI Tetsuko 井森徹子 井戸
リンクを右クリックし "Save link as" で、~/Ruby
に保存する。
これを利用して検索してみる。
まずはローマ字でパターンを指定してみよう。
Unixには正規表現を使ってファイルから特定の行を検索してくれる
egrep
というコマンドがある(Rubyとは別物)。
egrep
コマンドは
% egrep "正規表現パターン" [ファイル群…]
のように起動し、0個以上の複数のファイルから「正規表現パターン」にマッ チする行だけを選んで出力してくれる。ファイルを0個指定すると、 標準入力から読み込んだデータから検索する。
標準入力/標準出力に関してはとても大事な概念なので Unixひとめぐりページを よく読んでおくこと。
SAITOH または SATOH のいずれかを含む行を全部選んでみよう。 この場合、大文字の I があってもなくてもよいので、 正規表現パターンは "SAI?TOH" となる。
egrep "SAI?TOH" meibo.txt
正規表現のパターンを ""
で括っているのは、
途中にある?
記号をファイル名マッチだと思われないようにするため
である。このようにコマンドラインで正規表現パターンを指定するときは、パター
ンを""
または ''
で必ず括ること。
実行結果は次のようになるだろう。
egrep "SAI?TOH" meibo.txt
サトウ ムネユキ SATOH Muneyuki さとう胸幸 青葉台
サイトウ テツヤ SAITOH Tetsuya 斎藤徹夜 白夜の国
もう少し欲張って、楽になるように検索パターンを考えよう。
パターン指定にいちいち大文字を打つのは面倒だし、もしかしたらデータファイ
ルの方で Satoh と書いてしまう場合もあるので、大文字でも小文字でもどちら
でもマッチするようにしてみよう。検索のときに大文字小文字を区別しないため
には egrep
コマンドに -i
オプションを指定すれば
よい。
egrep -i "sai?toh" meibo.txt
サトウ ムネユキ SATOH Muneyuki さとう胸幸 青葉台
サイトウ テツヤ SAITOH Tetsuya 斎藤徹夜 白夜の国
カタカナで検索してみよう。「サトウ」でも「サイトウ」でもよいように、 「サイ?トウ」というパターンで検索してみよう。
Terminalでの日本語入力は Shift-SPC でON/OFFする。 カタカナへの変換は C-k で行なう。
egrep -i "サイ?トウ" meibo.txt
サトウ ムネユキ SATOH Muneyuki さとう胸幸 青葉台
サイトウ テツヤ SAITOH Tetsuya 斎藤徹夜 白夜の国
続いて、以下の条件にマッチする人を検索してみよう(パターンを "" で括るのを忘れずに)。
上記の検索実験は期待した結果と少し違うものが出てくることを 実感するのが目的である。
egrepと違い、Rubyでは正規表現を / /
で括って指定する。
たとえば、egrep
の例で指定した正規表現 "sai?toh"
はRubyでは、
/sai?toh/
と表記する。これに注意して、
「指定したファイルから sai?toh というパターンを
検索する」
Rubyプログラム saito.rb
を作成すると次のようになる(後述の説明のため途中から行番号を付した)。
#!/usr/koeki/bin/ruby # -*- coding: utf-8 -*- 1: while line=gets 2: if /sai?toh/i =~ line 3: print line 4: end 5: end
まずは、実際に実行してみよう。egrep
コマンドと同様
検索したいファイルを引数として与えよう(検索パターンはRubyプログラム内に
記述してあるのでコマンドラインからは指定しなくてよい)。
chmod +x saito.rb ./saito.rb meibo.txt サトウ ムネユキ SATOH Muneyuki さとう胸幸 青葉台 サイトウ テツヤ SAITOH Tetsuya 斎藤徹夜 白夜の国
プログラム(本体部分)の内容を一行ずつ見ていこう。
while line=gets
gets
はこれまで使ったことのあるメソッドである。こ
れまでは、キーボードから一行入力する例のみを示していたが、
必ずしもキーボードから入力するとは限らない。
Rubyプログラム実行時に何かのファイルを引数として指定するとそのファ
イルの中味を順次読むようになる。今回の場合
./saito.rb meibo.txt
と起動した。引数と
してmeibo.txt
を指定したので、gets
メソッ
ドは、meibo.txt
ファイルを1行ずつ読み込む。
つまり、一回目の line=gets
で
line="サトウ ムネユキ SATOH Muneyuki さとう胸幸 青葉台\n"
のような代入が起こり、二回目の gets
では
line="ナカマチ タロウ NAKAMACHI Taro 中町太郎 酒田\n"
となる(\n
は改行文字)。9行あるので、
while line=gets
は9回くり返すことになる。
gets
はこれ以上読むデータがなくなると
nil
を返し、while
ループは終了する。
if /sai?toh/i =~ line
このif
で、読み込んだ行が正規表現にマッチしているか
の判定を行なう。「正規表現にマッチしているか?」を意味する条件式は
正規表現 =~ 文字列
のように書く。マッチしている場合は、実際にマッチした部分が
文字列 の何バイト目かを表す整数を返す。マッチしない場合
はnil
を返し、条件不成立となる。
/sai?toh/i
は、Rubyによる正規表現の
指定である。後の /
の直後にある i
は
「大文字小文字を区別しない」という意味を付加する働きを持つ。つまり、
egrep
コマンドの -i
オプションは、Rubyで
は、正規表現の直後に i
を付けることに相当する。
正規表現の後ろの =~
は、「正規表現がマッチすれば…」
という比較演算子である。通常1行ずつ入力した文字列をこの右辺に書く。
ちなみに「マッチしなければ」を意味する演算子は
!~
である。
=~
の右辺に指定した「文字列」が正規表現の比較対象
となる文字列である。
print line
line
変数にはデータファイルから読み込んだ行が入っ
ているので、それを出力する。もちろん、一つ上の行にif
があるので、正規表現にマッチした場合だけ該当行が出力される。
end
if
に対応するend
end
while
に対応するend