roy > naoya > 基礎プログラミングI·情報検索 > (7)正規表現[1]
電話帳で「ミニマムバリュ」という店の電話番号を探す場合、50音順に並んでいるので「ミ」の場所をみれば良い。では「美作太郎商店」や「美篶形成外科」の電話番号はどのように調べればよいだろうか? 前者は「みさく」「びさく」のどちらだかわからないし(正解は「みまさか」)、後者については全く読めない(正解は「みすず」)。この様な場合は、先頭の文字から調べるのではなく、先頭はわからないが後ろが「太郎商店」や「形成外科」である店の番号という検索ができれば便利である。
電話で名乗られたときも同じようなことが考えられる。携帯電話から電話をしており電波状況が悪くうまく聞き取れない。おそらく「マエハシ」か「マエバシ」なのだが判断ができない。こんな場合は「マエ(ハまたはバ)シ」という名字の人という探し方ができれば便利である。「サトウ」と「サイトウ」の聞き分けが難しい場合には「サ(イ)トウ」という探し方が必要になるかもしれない。
何かを検索するときに、そのデータに含まれる文字列の一部やパターンを利用することがある。このパターンを表現するために使用するのが正規表現である。
正規表現とは
「FUKUSHI」と「FUKUSI」を探したい場合、正規表現では次のように検索パターンを書く。
FUKUSH?I
?は直前のHはあってもなくてもよいという指定になる。これで「FUKUSHI」と「FUKUSI」のどちらでも該当する。?以外にも[]を使って表現することもある。
スガ[ハワ]ラ
この場合「スガハラ」でも「スガワラ」でも該当する。[]の中に何文字か入れると、これらのうちのいずれかが含まれればよいという指定になる。
Rubyでは正規表現のパターンであることを指定するために//でくくって指定するので、正確には下記の通りとなる。
/FUJISH?IMA/
/スガ[ハワ]ラ/
正規表現のパターンを指定する際に使用する?や[]などの文字をメタ文字と呼ぶ。
指定したファイルから該当する文字列を含む行を検索するプログラムを作り検索してみよう。
まず、下記の架空の名簿をmeibo.txtという名前をつけて保存する(プログラムを作成する時と同じようにemacsに貼り付けて保存)。
丘本秋子 オカモトアキコ 酒田市 OKAMOTO Akiko 高梁缶 タカハシカン 真室川町 TAKAHASHI Kan 仁科牧子 ニシナマキコ 最上町 NISHINA Makiko 藤嶌楓 フジシマカエデ 庄内町 HUJISHIMA Kaede 町村利郎 マチムラトシロウ 鶴岡市 MACHIMURA Toshiro 市川週一 イチカワシュウイチ 遊佐町 ICHIKAWA Shuichi 多村数馬 タムラカズマ 西川町 TAMURA Kazuma 砂糖真琴 サトウマコト 山形市 SATO Makoto 藤嶋丸子 フジシママルコ 東根市 FUJISIMA Maruko 嶋田多香子 シマダタカコ 天童市 SHIMADA Takako 齋藤由 サイトウユウ 大江町 SAITO Yu 高橋麹 タカハシコウジ 村山市 Takahasi Koji 渡部すず ワタベスズ 米沢市 WATABE Suzu 渡辺勇次 ワタナベユウジ 小国町 WATANABE Yuji 藤島真希 フジシママキ 寒河江市 FUJISHIMA Maki
次に、以下のプログラムをkensaku.rbという名前をつけて保存する。
#!/usr/koeki/bin/ruby while line=gets if /sai?to/i =~ line print line end end
保存をしたら実行してみる。実行する際は、いつもとは若干方法が異なり、検索対象となるファイル名を指定する必要がある。以下では、ruby kensaku.rbの右にmeibo.txtと書いてある。検索対象としてmeibo.txtというファイルを指定していることになる。
irsv{c10xxxx}%ruby kensaku.rb meibo.txt[Return] 砂糖真琴 サトウマコト 山形市 SATO Makoto 齋藤由 サイトウユウ 大江町 SAITO Yu
続いてプログラムの各行について解説する。
getsはキーボードから入力された値を文字列として取得するメソッドとして利用してきた。しかし、今回のプログラムを実行しても入力は求められなかったはずである。getsメソッドはキーボードからの入力を受け取る以外に、ファイル内容の読み込みも行うことができる。プログラム実行時にファイルを指定しなければキーボードからの入力を受け取り、ファイルを指定するとファイルの中味を読み込む。今回は実行時に、meibo.txtを指定しているのでこのファイルの中味を読み込む。
getsメソッドがキーボードからの入力を受け取る場合、入力後に必ず[Return]を押す。[Return]はUNIXでは\nという改行文字として受け取られる。getsメソッドがファイルから読み込みを行う場合も、同様に\nを区切りとしてとらえる。すなわち行末の改行を区切りとして1つのデータとして読み込み、ここでは変数lineに代入する。
while line=getsはデータを読みこめる間は繰り返し処理を行えという意味になる。meibo.txtは15行あるので、15回繰り返され、16回目になると読み込むデータがなくなるので繰り返しから抜ける。
この行で読み込んだ行が検索パターン(正規表現)にマッチしているかどうか判定を行う。正規表現にマッチしているかどうかは
/正規表現/ =~ 文字列
と表記する。=~は「正規表現にマッチする」という比較演算子である。反対に「正規表現にマッチしない」は!~とあらわす。
if /sai?to/i = ~lineでは、検索パターンは「sai?to」である。これは「saito」もしくは「sato」をあらわす。//の後ろにあるiは大文字と小文字を区別しないという意味を付加するもので、必要に応じてつけたり、つけなかったりする。iをつけない場合は「sato」はマッチするが、「SATO」「Sato」「sAtO」などはマッチしなくなる。
lineという変数には、ファイルから読み込んだ行が入っている。上記の条件に合致する場合に、その行が出力される。
ifに対応するend。
whileに対応するend。
正規表現を用いたプログラムに関するまとめ
/正規表現/の後ろにiは大文字と小文字の区別をしないというオプションであるが、これは半角英数字を検索対象とする場合に有効なオプションである。日本語にマッチさせるためには/の後ろにiのかわりにeまたはsをつける必要がある。
正規表現のオプション
meibo.txtにおいて以下の検索を行うための正規表現のパターンを考えてみよう。
制限時間は10分。完成しない場合は、途中まででも構わないので実行し、結果をメールで送ること。出席点は2点。提出要領は下記の通り。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:Mewによるメールの送り方はMewコマンドを参照
正規表現によるパターンが英数字や日本語のみで書かれている場合、文字列内にその文字が含まれていればマッチする。以下にサンプルを示す。正規表現にマッチする文字列は黄色の網掛けで示し、マッチしている部分を赤色で示す。
正規表現:/ABC/(意味:ABCを含む文字列)
#=> ABC、 ABCDEF、 123ABC、 A1B2C3、AB、abc
ABCから始まる文字列や、最後がABCで終わっている文字列を検索する場合は、「^」や「$」といった特殊な文字(メタ文字を使用する。「^」は行頭マッチング、「$」は行末マッチングをあらわす。
正規表現:/^ABC/(意味:ABCで始まる文字列)
#=> ABC、 ABCDEF、 123ABC
正規表現:/ABC$/(意味:ABCで終わる文字列)
#=> ABC、 ABCDEF、123ABC
イトウもしくはゴトウのように「イかゴ」のどちらかを含むという条件を指定する場合、マッチさせたい文字の集合を[]で囲む。[]内のどれかが該当すればマッチすることになる。
正規表現:/[ABC]/(意味:A、B、Cのいずれかを含む文字列)
#=> DNA、 Book、 BAC、 Cat、 DOT
正規表現:/[CBA]/(意味:A、B、Cのいずれかを含む文字列。上と同じ)
#=> DNA、 Book、 BAC、 Cat、 DOT
「大文字のアルファベットが含まれていればなんでもOK」というような正規表現を指定する場合、○−○というようにハイフンを使って範囲で指定することができる。
正規表現:/[A-Z]/(意味:アルファベットの大文字を含む文字列)
#=> 028A、 Book、 Cat、 dog、075、6-4
正規表現:/[a-z]/(意味:アルファベットの小文字を含む文字列)
#=>028A、 Book、 Cat、 dog、 075、6-4
正規表現:/[0-9]/(意味:数字を含む文字列)
#=> 028A、 Book、Cat、dog、 075、 6-4
正規表現:/[A-Za-z_-]/(意味:アルファベットとアンダーバーとハイフンを含む文字列)
正規表現:/[ぁ-ん]/(意味:ひらがなを含む文字列「ぁ」は小文字)
正規表現:/[亜-腕]/(意味:漢字を含む文字列。ただし第一水準のみ)
ハイフンは範囲を示すために使用するので、ハイフン自体を検索対象の文字列としたい場合は、最初か最後に書かなければならない。
なお行頭マッチングで使用した^は[]内で使用するとそこで指定されたもの以外の文字という意味になる。例えば、[^ABC]はA、B、C以外の文字ということになる。
正規表現は組み合わせて使用することができる。ここまでに出てきたものを組み合わせると以下のようなものを作ることができる。
正規表現:/a[ABC]c/(意味:aとcの間にA、B、Cのいずれかがある文字列)
#=> aBc、 1aBcDe、 abc
正規表現:/a[^A-C]c/(意味:aとcの間がA、B、C以外の文字である文字列)
#=> aBcabc、 a0c、 malcolm、 aCc
正規表現:/[0-9][A-Z]/(意味:数字の次に大文字のアルファベットが続く文字列)
#=> 0A、 000AAA、254XBJ
正規表現:/[^A-Z][A-Z]/(意味:アルファベットの大文字以外の文字の次にアルファベットの大文字が続く文字列)
#=> aA2B3C、 NH068A
.(ピリオド):任意の1文字にマッチする。ピリオド1つなら1文字の文字列、3つなら3文字の文字列をあらわす。
正規表現:/A.C/(意味:AとCの間に何か一文字ある文字列)
#=> ABC、 012A3C456、 AA、 AC、 ABBC、 abc
正規表現:/aaa.../(意味:aaaの後に何か三文字続く文字列)
#=> 00aaabcde、 aaabb
*:直前の文字の0回以上の繰り返しにマッチする。
正規表現:/AAA*C/(意味:Cの前にAが2個以上ある)
#=> 012\nAAC、 012AAAAAAAAC、 AC
正規表現:/A.*C/(意味:AとCの間に何かが何個あってもいいし、なくても良い)
#=> AB012C、 AB CD、 ACDE
+:直前の文字の1回以上の繰り返しにマッチする。
正規表現:/A+C/(意味:Cの前にAが1個以上ある)
#=> AAAC、 BAC、 BC、 AAAB
正規表現:/A.+C/(意味:AとCの間に何かが1個以上ある)
#=> AB012C、 AB CD、 ACDE
?:直前の文字の0回または1回の繰り返しにマッチする。
正規表現:/AAA?C/(意味:Cの前にAが2個以上ある)
#=> AAAAAC、 AAC、 AC
正規表現:/A.?C/(意味:AとCの間に何かがない、もしくは1文字ある)
#=> ACDE、 ABCDE、 AB012C、 AB CD
():*+?と組み合わせて使用することで,1文字単位での繰り返しではなく、複数の文字列の繰り返しにマッチする。
正規表現:/(ABC)+/(意味:ABCが1回以上繰り返されている)
#=> ABC、 ABCABC、 ABCABCABC、 BCA
正規表現:/HOTO(TO)?GISU/(意味:TOが0回もしくは1回)
#=> HOTOTOGISU、 HOTOGISU、 HOTOTOTOGISU
|:いくつかの候補の中からどれか1つに当てはまるものにマッチする。
正規表現:/(sakura|kashiwa)mochi/(意味:mochiの前にsakuraもしくはkasiwaがある)
#=> sakuramochi、 kasiwamochi、 awabimochi、 yakimochi
\s(バックスラッシュスモールエス):空白文字をあらわす。空白、タブ、改行文字とマッチする。
正規表現:/ABC\sDEF/(意味:ABCとDEFの間に空白が一つある文字列)
#=> ABC DEF、 ABC\tDEFGH、 123ABC\nDEFG、 ABCDEF
\S(バックスラッシュラージエス):空白以外をあらわす。
正規表現:/ABC\SDEF/(意味:ABCとDEFの間に空白以外の文字が一つある文字列)
#=> ABC3DEF4GHI、 012-ABC-DEF、 ABC DEF、 ABCDEF
\d(バックスラッシュスモールディー):0から9までの数字とマッチする。
正規表現:/\d\d\d-\d\d\d\d/(意味:ハイフンの手前に数字が3桁、後ろに4桁あるもの)
#=> 012-3456、 01234-012345、 ABC-DEFG、 012-21
\D(バックスラッシュラージディー):0から9までの数字以外とマッチする。
正規表現:/\D\d\d\d\d\d\d\D/(意味:数字以外の文字の間に数字が6桁並んでいるもの)
#=> c106999a、 c1068887
\w(バックスラッシュスモールダブリュー):英数字と_(アンダーバー)にマッチする。
正規表現:/\w\w\w/(意味:英数字が3つ連続で続いている文字列)
#=> ABC、 abc、 012、 AB C, AB\nC
\W(バックスラッシュラージダブリュー):英数字と_(アンダーバー)以外にマッチする。
正規表現:/A\WB/(意味:AとBの間が英数字以外の文字列)
#=> A C、 A-C、 ABC、A9C
\A(バックスラッシュラージエー):文字列の先頭にマッチする。^との違いは、^は画面上に出力されている状態で先頭にあればマッチするが、\Aはひとかたまりの文字列の先頭であるかどうかを見る。具体的には、123\nABCは文字列の途中に改行文字が入っているが、/^ABC/はマッチするのに対し、/\AABC/はマッチしない。
正規表現:/\AABC/(意味:先頭がABCである文字列)
#=> ABC、 ABCDEF、 012ABC、 012\nABC
\Z(バックスラッシュラージゼット):文字列の末尾にマッチする。$との違いは、$が画面上に出力されている状態で末尾にあればマッチするが、\Zはひとかたまりの文字列の末尾であるかどうかを見る。具体的には123\nABCは文字列の途中に改行文字が入っているが、/123$/はマッチするのに対し、/123\Z/はマッチしない。
正規表現:/ABC\Z/(意味:末尾がABCである文字列)
#=> ABC、 012ABC、 ABCDEF、 012\nABC、 ABC\nDEF
\b(バックスラッシュスモールビー):単語の境界にマッチする。例えば/cat/で検索すると、concatenateやcategoryもマッチするが、\bcat\bにすると、単語として独立したcatしかマッチしない。
正規表現:/\bdog\b/(意味:dogという単語を含むもの)
#=> dog、 hot dog、 dogberry
\:ピリオド、[]、?、*、+、^、$、|、()などのメタ文字として使用されている文字を検索対象としたい場合は手前にバックスラッシュをつけ、\.、\[、\]、\?、\*、\+、\^、\$、\|、\(、\)とする。バックスラッシュを検索対象とする場合も\をつけ\\とする。
メタ文字は組み合わせて使用することができる。幾つか例を示す。
自分で自由にリストを作り、そのリストから特定の正規表現のパターンにマッチする行を取り出してみる。いろいろなパターンについて試し、その中でも複雑な5つのパターンについて実行した結果をレポートする。具体的には下記の流れにしたがうこと(8点満点)。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする
Tips:Mewによるメールの送り方はMewコマンドを参照