正規表現をどう使う

テキスト処理では正規表現は必須の機能と言えます。ここでは正規表現のお話をしましょう。

手書きの文章ではとかくミスが付き纏います。英文の論文などの氏名綴りでは、文字抜けや文字違いなどが多く発見されます。
このような曖昧な文字列から、検索や置換をするときに正規表現はまことに便利な機能です。

正規表現では「メタ文字」と呼ばれる記号の組み合わせで文字列を表現します。
以下にAWKで使われるメタ文字の一部を紹介いたします。

  1. . (ピリオド)
    任意の1文字を表します。
  2. *
    直前の文字の 0 回以上の繰り返しを表現します。ピリオドと組み合わせてDOSの「ワイルドカード」と同じ表現ができます。
  3. +
    直前の文字の 1 回以上の繰り返しを表現します。ワイルドカードとは異なり直前文字が 1 個は必要となります。
  4. x|y
    正規表現 x か y にマッチするようになります。
  5. [xyz]
    [ ] で囲まれた何れか 1 文字を表します。
  6. \
    次に続く文字がメタ文字の場合、その文字そのものを表します。
さて、これらのメタ文字を使用してある文字列にマッチングする正規表現をどのように記述するか・・・ですが、ここで重要なことをお話ししなければなりません。
それは・・・
このことを意識して正規表現を組み立ててください。


それでは、正規表現の実験をしましょう。
以下のようなスクリプトをエディタで作成して「regexp.awk」と言うファイル名で保存してください。
さらに、このディレクトリに gawk.exe と、このスクリプトをAWKで実行させるようなバッチファイルも作成してください。
バッチファイルは「変数のスコープ」の章で作成していますので参考にして下さい。

BEGIN {
  str = "The compounds can be prepared by forming the sodium salt of the indazol-3-ol by effecting a Williamson Ether Synthesis reaction with carb-lower alkoxymethyl halide.The compounds possess anti-inflammatory activity as demonstrated by the Limb Volume Test procedure.";
  pos = match(str, /com.+ds/)
  print "変数が返す値: " pos;
  print "マッチした文字位置: " RSTART;
  print "マッチした文字列長: " RLENGTH;
  print "マッチした文字列: " substr(str, RSTART, RLENGTH);
}
BEGINブロック第1行目は、検索対象の文字列を変数 str に代入しています。長い文字列ですね。全体が見えないときは横スクロールしてください。
第2行目は match 関数です。この関数の第1引数は対象文字列、第2引数はマッチングさせる正規表現です。(正規表現は / / で囲む)
match 関数の戻り値は、見つかればマッチした文字位置を、マッチしなければ 0 を返します。文字列先頭にマッチすれば 1 です。
ここでは「com」を先頭に 1 文字以上の文字(「.」は何かの文字、「+」は 1 文字以上)に続き「ds」までの文字列にマッチさせようとしています。
具体的には「compounds」のつもりですが・・・
第3行目は変数が返す値を表示する print です。
AWKでは文字列の連結は空白文字で区切って並べればOKです。ここでは「変数が返す値: 」と変数 pos の内容を連結しています。
同様に第4、第5行目は、それぞれ予約変数 RSTART と RLENGTH の値を表示させます。
RSTART はマッチした文字位置、RLENGTH はマッチした文字列の長さが代入されています。
第6行目は substr 関数を使って正規表現がマッチした文字列を表示します。

バッチファイルをダブルクリックして実行させると、以下のように表示されます。
変数が返す値: 5
マッチした文字位置: 5
マッチした文字列長: 173
マッチした文字列: compounds can be prepared by forming the sodium salt of the
ndazol-3-ol by effecting a Williamson Ether Synthesis reaction with carb-lower
lkoxymethyl halide.The compounds
続行するには何かキーを押してください . . .
あれ!! かなり長い文字列にマッチしましたねェ。。。
「compounds」にマッチさせたかったのですが失敗です。
これが「最左最長」のルールです。AWKはいちばん長くマッチする文字列を探してしまったのです。

それでは正規表現を以下のように書き換えてください。
  pos = match(str, /com[a-z]+ds/)
[ ] で囲まれた何れかの文字にマッチするのですが、ここでは a-z としています。このように指定すると a から z までの何れかの文字にマッチします。
英小文字だけにマッチさせようという魂胆です。
実行すると・・・
変数が返す値: 5
マッチした文字位置: 5
マッチした文字列長: 9
マッチした文字列: compounds
続行するには何かキーを押してください . . .
やったぁ〜〜 OKですね。
以上、「最左最長」のお話でした。

えっ、後方の「compounds」にマッチさせるにはどうするか・・・ですって。。。
一発では無理です。
ですが、while 文などで「マッチしたらそれ以降の文字列を切り出してまたマッチングさせる」・・・をループさせれば可能です。
ちょっと面倒ですが、一つの文字列から複数のマッチした文字列位置を知ることができます。
論文などのように 1 行が長い文字列からキーワード検索するなど、結構多くの場面で必要になります。
たぶん、今後アップされる(であろう)サンプルにも何気なく?使われているでしょう。


次に、「-」が含まれるワードを検索する例をご紹介いたします。
先のスクリプトの正規表現部分を以下のように書き換えてください。

  pos = match(str, /[a-zA-Z]+-[a-zA-Z]+/)
ここで「[a-zA-Z]+」は、英文字だけ(a から z か A から Z の何れかの文字の 1 回以上の繰り返し)で構成されているワードに一致する正規表現です。
ですから「[a-zA-Z]+-[a-zA-Z]+」は、「-」の前後が英文字ワードである文字列にマッチします。
これを実行すると・・・
変数が返す値: 134
マッチした文字位置: 134
マッチした文字列長: 10
マッチした文字列: carb-lower
続行するには何かキーを押してください . . .
・・・となります。
しかし、「indazol-3-ol」にはマッチしていませんね。
数字の「3」があるのでマッチしないのです。
「[a-zA-Z]+」の部分を、数字も含めて「[a-zA-Z0-9]+」にすれば「indazol-3」にマッチするはずです。実験してみてください。
ついでに、「[a-zA-Z0-9-]+」と、「-」自身も含めれば「indazol-3-ol」にきちんとマッチすることも確かめてください。

ご注意!!
AWKによっては、「-」の扱いに問題があるような場合があります。
念のため「[a-zA-Z0-9\-]+」のように「\」で「-」がハイフンそのものであることを明示的に示せば安心でしょう。

これで、「正規表現」のお話は終わりにさせて頂きます。
他のメタ文字の使い方に関してはこちらを参照してください。右端の「※」をクリックすると詳細説明を見ることができます。
ただし、JavaScript 用ですので正規表現部分である「/…/」を見ながら読んでください。

<-->

戻る