過去ブログの転載です。
文字列照合を行う際に便利な文字列の表現に正規表現というものがあります。これを知っておくと、データベースの中から目的のパターンを持った文字列を検索するのがとってもラクになります。
便利な割にちょっと慣れないととっつきづらいので、プログラミングではなじみ深い表現ですが、あまり世間一般では聞かないものなようにも思います。プログラミングじゃなくても、検索ツールが正規表現に対応していれば、それを使って高度な検索ができて便利なんですよ。
例えばこういう文字列を検索したいとき……
(「総長」か「ジュラフェ」)が(何か3文字)を食べた(「。」はあってもなくてもいい)
色々なパターンの文字列が考えられます。
総長がアイスを食べた。
ジュラフェがプリンを食べた(←「。」はなくてもOK)
など。
何とか上の色々な形式を一つの文字列で表現することはできるでしょうか。
これを正規表現を用いて表記すると、こうなります。
(総長|ジュラフェ)が...を食べた。?
呪文みたいですね。
検索文字列としてこの呪文のような表記を入れてやれば、正規表現に対応した検索エンジンだとそれらにマッチする文字列を全部検索してくれるということになります。便利じゃーありませんか。
正規表現の文法
色々ありますが、よく使われると思われるものを列挙します。正規表現で使われる各種記号はすべて半角です。
. : 何か1字
* : 0回以上繰り返し
+ : 1回以上繰り返し
? : 0回か1回
{} : 数量詞
| : どれか
() : グループ化
: 範囲内のうちどれか
^ : 行の最初, 否定
$ : 行の最後
一つずつ紹介していきます。
. : 何か1字
何でもいい文字を意味します。ワイルドカードですね。
.は.を.に..した
とか書いたら
「私は金を道に落とした」
「彼は女を家に誘導した」
とかを検索することができますが、
「私は金を道路に落とした」
とかはマッチしませんので、検索結果に出ません。
* : 0回以上繰り返し
直前の文字が0回以上の繰り返しであるときに使います。
ほんと!?*
とか書いたら
「ほんと!」
「ほんと!?」
「ほんと!??」
「ほんと!???????」
とかにすべてマッチします。
さらに、さきほどの . と組み合わせて
.*とすれば、何でもいい文字列が0回以上続くという意味にできます。
私が食べたいのは「.*」です
とか書いたら、
私が食べたいのは「寿司」です
私が食べたいのは「オムライス」です
私が食べたいのは「きのこ」です
とか、何でも当てはまるようになります。
+ : 1回以上繰り返し
*とは違い、こちらは1回以上の繰り返しです。
わ~+い!
と書くと、
わ~い!
わ~~い!
わ~~~~~~い!
にはマッチしますが、
わい!
にはマッチしません。
? : 0回か1回
こちらは直前の文字があるかないか、というパターンを表わします。
冒頭の例でいう
食べた。?
というのは、?の直前の「。」があるかないかの2通りということで
食べた
食べた。
のどちらかにマッチするものとすることができます。
{} : 数量詞
直前の文字がちょうどn回マッチする文字と決めたい場合、{n}とつけてやれば良くなります。
w{3}
とすれば、
www
がマッチします。
更に、回数を範囲指定することもできます。
石{2, 5}
とすると、
石石
石石石
石石石石
石石石石石
のどれかが当てはまります。
また、末端の数を省略して
石{2, }
とか書くと、2回以上という意味になります。
| : どれか
| で区切った文字列のどれか、という意味です。
ベイ助|ベイ太|ライ太
とすれば、
ベイ助
ベイ太
ライ太
のどれかがマッチします。
() : グループ化
グループとしてまとめるときにはこのかっこを使います。
(総長|ジュラフェ)が
とすることで、
総長が
ジュラフェが
とマッチさせることができ、
「総長」「ジュラフェ」のいずれかとしてまとめることができます。
また、繰り返しでももちろん適用できまして
(ぴん)+くり
と書けば、
ぴんくり
ぴんぴんくり
ぴんぴんぴんくり
とかとマッチさせることができます。
: 範囲内のうちどれか
の範囲内にある文字のうちのどれかが採用されます。
[水金火土木]星
と書くことで、
水星
金星
火星
土星
木星
とマッチさせられます。
(水|金|火|土|木)星 と同じですね。
ただ、を使うとこのように文字を範囲で指定することができます。
[A-Z] : AからZ(半角大文字アルファベット)
[0-9] : 0から9(半角数字)
[か-こ] : か, が, き, ぎ, く, ぐ, け, げ, こ のどれか
さらに組み合わせて、
[A-Z0-9] : AからZか、0から9
こうやってまとめて扱うときとかは便利ですが、文字コード基準なので日本語を範囲指定するとおかしな範囲になってしまいそうですね。
特殊文字として、\dは[0-9]と同じ意味だったり、\wは[a-zA-Z0-9_]と同じ意味だったりします。こういうのを使う際には便利ですね。
^ : 行の最初, 否定
[]の中で先頭に^を付ければ否定を表わします。[^A-Z] で、AからZ以外の文字、など。
一方で同じ記号で、^をつけると行の最初という意味にもなります。
例えば「きのこ」を検索文字列とすると
私はきのこが好きです
というように行の途中からでもマッチするのですが、「^きのこ」を検索文字列としたらこれはマッチせず、
きのこが好きです私は
などというように、行の最初にあるもののみマッチさせることができます。
$ : 行の最後
こちらは^とは逆に、行の最後を意味します。「きのこ$」を検索文字列とすると、
私が好きなのはきのこ
にマッチさせることができます。
「^そんなことはない$」とすると、行の最初と最後が固定されますので、これは「そんなことはない」以外のパターンとマッチしません。
練習
正規表現を図で表わしてくれるサイトがあります。
こちらのサイトで、冒頭にあった
(総長|ジュラフェ)が...を食べた。?
を入力させてみると、次のような図が出てきます。
「総長」か「ジュラフェ」を通ってから「が」を通り、
「any character(何かの文字)」が3個通って、「を食べた」、
「。」が0回か1回、というのは、「。」を通るか通らないかの
2通りの道がある、というようにして表現されています。
これは分かりやすいですね。書いた正規表現が正しいかどうか確認するときにとても役に立ちます。
正規表現を練習してみましょう。3問あります。先ほどのような図を見て、正規表現を考えてみてください。
Q1.
Q2.
One of:「"1"-"9"」は、1~9のうちどれか1字という意味です。
Q3.
以下、こたえです。
Q1. こたえ
(ベイ助|クー太|ベイ太)「もう、?食べられないよ!」
Q2. こたえ
「オイラが好きなのは.*で、一日に[1-9][0-9]*個は食べるんだ」
[1-9][0-9]*個とすることで、はじめの1~9は必ずきて、そのあと任意で0~9の数字がいくつかつくことになり、これにより「正の整数」を表わすことができます。
Q3. こたえ
[1-9]日(前|後)は(元旦|(山|海|体育)の日)である。
(前|後)は、1文字だから[前後]でもとりあえず同じです。
わかったかな??