---------- SED 教室 第四回 「正規表現」 ---------- SED のスクリプトは「実行条件」と「命令」からなるということは既に説明し ました。今回は「実行条件」の全部を一気に紹介してしまいます。といっても後 二種類しかないのです。^^;) まず、既にやったのは 数字 読み込んだ行が、「数字」で示された行か? $ 読み込んだ行が、最終行か? 条件1,条件2 「条件1」が成立する行から「条件2」が成立する行までの間か? でしたね。あとは次の二つです。 条件! 「条件」が成り立たない行か? /正規表現/ 「正規表現」にマッチする行か? 条件! は何となくわかるのではないでしょうか。たとえば 5! だと 「5 行目 以外」の行で条件成立となります。 問題はその次ですね。一体「正規表現」とは何ものでしょう。「正規表現」は "regular expression" の訳なんですが、MS-DOS のワイルドカード (ファイル を * や ? を使って指定する方法ですね。 DEL *.* なんてコマンドラインで入 力するとファイルを全部消してくれます。) みたいなものと考えれば、まあ当た らずとも遠からずというところでしょう。 たとえば「S?D.*」というワイルドカードは「SED.EXE」や「SED.DOC」などと マッチしますが、正規表現も同じような感じで特定の行とマッチするのです。と いうことはワイルドカードの「*」や「?」に相当するものを覚えれば良いわけで す。なあんだ、簡単じゃないか、と思うのは早計です。正規表現はワイルドカー ドに比べてその能力が極めて高いのです。能力が高いというのはどういうことか というと、たとえば私のダイナブックのあるディレクトリを覗くと下のようにな ります。 装置 S: のディスクのボリュームラベルは HARD-RAM ディレクトリは S:/LESS . .. RCS CDOS A86 DOSLIB A86 LESS C LESS H CDOS OBJ DOSLIB OBJ LIB H MAKEFILE LESS OBJ LESS SYM README SHOWLINE C SHOWLINE OBJ LESS MAP 17 個のファイルがあります. 1259520 バイトが使用可能です. この中から、拡張子が .C と .H 以外のものを全部消したいという場合を考え てみましょう。 .C と .H 以外のものですと、.OBJ .MAP .SYM .A86 それに README と MAKEFILE ですから、 DEL *.OBJ *.MAP *.SYM *.A86 README MAKEFILE とすれば良いのですが、このようなことは .C と .H 以外のファイルにはなに があるかを知っているから出来ることであって、ひょっとしたら他に LESS.EXE というファイルがあるかも知れませんし、LESS.DOC などが作られることがある かも知れません。すると、「.C と .H 以外のものを全部消す」という要求は満 たすことが出来なくなるわけです。どんなファイルがあるのかなんて見りゃわか るだろう、などと言っちゃ駄目ですよ。人間の頭という「超」コンピュータを使 ってしまえば、もっともっと高度なことが幾らでも出来るのですから。あくまで も、コンピュータに「自動的に」やってもらうのが目的なのです。そして、ワイ ルドカードは「.C と .H 以外のもの全部」という表現が出来ないので、能力が 低い事になります。 正規表現はもともと有限オートマトンで認識可能な言語集合に対応するもので あるということになっていたのですが、コンピュータの世界では何故かはるかに 拡張された表現に対して「正規表現」という言葉が使われるようになってしまっ ています。個人的には他の名前にした方が紛らわしくなくて良いと思っています。 あ、すみません。つい私の専攻の分野なのでよけいなことを話してしまいました。 要するに「正規表現」は強力なんだなと覚えておいてください。 ではまず簡単なところから紹介して行きましょう。正規表現「abc」は文字列 「abc」を表します。任意の 1 文字を表すには「.」(ピリオド) を使います。た とえば「a.c」は、文字列「abc」,「axc」, [a@c」などを表します。ワイルドカー ドの「?」と同じですね。ピリオドを含む文字列を表したいときは、「Mt\. Fuji」 などと指定します。このように正規表現で特別の意味を持っている記号を文字と して使いたいときは、前に「\」をつけます。「\」自体を文字として使うときは 「\\」とすれば良いですね。 さて、たとえば文字列「atc」は正規表現「a.c」が表現する文字列のうちの一 つですが、この時正規表現「a.c」は文字列「atc」にマッチすると言います。そ して SED が標準入力から読み込んだ行に、正規表現がマッチするような文字列 が含まれるとき、この正規表現はその行にマッチします。つまり条件が成立する わけです。たとえば読み込んだ行が「XYZabcxyz」だとすると、条件「/a.c/」は 成立します。 ワイルドカードの「*」(任意の文字列を表す) に対応する正規表現の記号はな にかといいますと、「.*」になります。なんで二文字なのかですが、「*」は直 前の正規表現の 0 回以上の繰り返しという意味なのです。つまり、「.*」は 「..」とか「....」とか 0 個以上の「.」が表す文字列のすべてを表すわけです。 任意文字が任意個つながったものですから任意の文字列ですね。 では「a*b」はどんな文字列を表すでしょうか。たとえば「aaaab」とか 「aaaaaaaaaaaaaaaaab」とかを表します。簡単ですね。それでは「b」にはマッ チするでしょうか。「*」は直前の文字の 0 個以上の繰り返しですから 「a」が 0 回繰り返されているということでマッチするのです。 どんどん進めましょう。次は「[複数の文字]」です。たとえば「[abc]」など と書きますとこの正規表現は「a」または「b」または「c」のいずれにもマッチ します。つまり [ ] の中の複数の文字のどれでも良いというときに使います。 みなさんは Nifty のログを整理するとき、会議室の発言のタイトルだけ抜き 取りたいと思った事はありませんか? タイトルは例えば次のようになっていま す。 「003/009 GCD03723 最大公約数 SED 教室その 1」 最初に数字が 3 つあって次が / そしてまた数字が 3 つですね。ここの会議 室の上限は 512 だそうですからタイトル行にマッチする正規表現は次のように なります。「/」は SED で正規表現を囲む事に使ってますので、前に「\」をつ けて区別します。 [012345][0123456789][0123456789]\/[012345][0123456789][0123456789] うーむ長い。いちいちこんな長いのを書くのは面倒ですので、次のように省略 して書く事ができます。 [0-5][0-9][0-9]\/[0-5][0-9][0-9] 同様にして、小文字のアルファベット一文字を表す正規表現は「[a-z]」にな ります。 さあ、では実際に正規表現を使ってスクリプトを書く事にしましょう。 <<< FIND.SED >>> ---------------------------------------- /[0-5][0-9][0-9]\/[0-5][0-9][0-9]/p d ---------------------------------------- SED がタイトル行を読み込んだときはスクリプトの一行目の条件が成立します から、命令「p」が実行されて、その行を標準出力に吐き出します。タイトル行 でない時は 一行目の条件は成り立ちませんから何もしません。次に二行目は命 令「d」ですからパターンスペースの内容を捨てて次の行を読み込み、スクリプ トの一行目へジャンプするのでしたね。 ではやってみましょう。 |W:/>sed -f find.sed < mes(17).0xx ← 会議室の発言のファイルです。 |001/009 NBF01152 ☆れお☆ SED講座はここでやりましょ! |002/009 NBF01152 ☆れお☆ 失礼!間違えました! |003/009 GCD03723 最大公約数 SED 教室その 1 | << 中略 >> |「003/009 GCD03723 最大公約数 SED 教室その 1」 あれ?最後のはなんだ? そういえばさっきタイトルを引用しましたね。この ように正規表現はマッチする文字列を含む行全部とマッチしますから、括弧でく くって引用していてもマッチしてしまうのです。ではどうしたら良いのでしょう か。「[0-5][0-9][0-9]\/…」と行頭から書かれている行にマッチさせる事にし ましょうか。そのためには << FIND.SED >> の一行目を次のように変えます。 /^[0-5][0-9][0-9]\/[0-5][0-9][0-9]/p 最初に「^」がありますね。この記号はここがパターンスペースの先頭である という事を示します。つまり SED が標準入力から 1 行分パターンスペースに読 み込んだとき、パターンスペースの最初から「[0-5][0-9][0-9]\/…」となって いる時のみ条件が成立するのです。「^」の逆も紹介しておきましょう。「$」は パターンスペースの最後を示します。従って行末になにか特定の文字列がある行 を探したいときに使えます。 さて、[0-9] が任意の数字を表す事ができるとなると、今度は数字でない文字 を表す方法があったら良いと思いませんでしょうか。当然ありまして、「[^0-9]」 とします。abc 以外の文字でしたら「[^abc]」と書きます。なんともややこしい 話ですが、[ ] の中に現れるときは「^」の意味が変わってしまうのです。でも パターンスペースの先頭である事を意味するときは必ず正規表現の最初に現れる から混乱する事はないでしょう。 前述した << FIND.SED >> は一行目に書かれた正規表現とマッチする行だけ標 準出力に吐き出しますので、いろいろな正規表現を一行目に書いてどんな行とマ ッチするか実験してみてください。いろいろな表現を試してみると正規表現がど のようなものかが良く分かるのではないかと思います。 --- GCD03723 (Greatest Common Divisor:最大公約数)