正規表現

Last-modified: 2008-09-02 (火) 23:19:01

正規表現は,文字列のパターン照合(パターンマッチング)を行う強力な機能です.SASには,前方一致演算子(=:)や,含まれる部分文字列の位置を返すINDEX関数などの,文字列操作機能はありましたが,正規表現を使うことで,より柔軟な操作を行うことができます.
正規表現の概念がはじめてという人に簡単に説明しますと,たとえば,Aで始まり拡張子がdocであるファイル名を表すのに,「A*.doc」と表すようなものです.ここででてくる「*」は,任意の文字列に該当する記号で一般にワイルドカードと呼ばれるものですが,正規表現では,ワイルドカードをさらに多機能にしたメタキャラクタと呼ばれる記号が用意されており,任意の文字とメタキャラクタを使って,目的とする文字列のパターンを表現します.
次の例では,正規表現(<.*?>)を使って,<>で囲まれたhtmlタグを見つけ,s///演算子でnullに置換,すなわち,タグの削除をしています.

data;
  text='<TD COLSPAN="2"><SMALL>■<B>注意事項</B></SMALL></TD>';
  text2=prxchange('s/<.*?>//', -1,text); /*<tag>をとる*/
  put text2=;
run;
/*
text2=■注意事項
 */

このように,複雑な文字操作ができる正規表現は,さまざまなコンピュータ言語に組み込まれていますが,言語によって若干の仕様の違いもあります.SASにも,SAS8に用意されたSAS独自のものと,SAS9から加わった,Perl仕様の2種類があります.Perl言語の正規表現は,もっとも広く使われているもののひとつといえますので,ここでは主にPerl仕様の正規表現を解説します.

Perl正規表現(PRX)の仕様

パターン照合と置換

SASの扱えるPerl正規表現には,パターン照合を行うm//演算子と,照合文字列を置換するs///演算子の2つがあります.
照合したいパターンは,「m/パターン/」ないしは,「s/パターン/置換/」と記述し,さらにこれにオプションがつけられるので,

  • m/パターン/オプション
  • s/パターン/置換/オプション
    のいずれかの形をとります.前者は,照合位置を返す関数PRXMATCHに使い,後者は,置換を行う関数PRXCHANGEに使います.
    なお,m//演算子は,mを省略して,「/パターン/オプション」と書くこともできます.また,パターンの中で「/」を使いたいことを想定して,
    「/」以外の記号を代用することもできます.たとえば,「m|パターン|オプション」とか「m+パターン+オプション」などと書く事もできますが,この場合はmを省略することはできません.
    オプションには以下のものがあります.
    オプション機能
    /i大文字と小文字を区別しない
    /m文字列を複数行として扱う
    /sピリオド(.)が改行にもマッチ
    /x拡張構文を使う
    data;
      str='Hello World';
      pos=prxmatch('m/world/',str);
      put pos=;
      pos=prxmatch('m/world/i',str); /*大文字と小文字を区別しない*/
      put pos=;
    run;
    /*
    pos=0
    pos=7
     */

パターン(正規表現)

パターンとは,照合したい文字列の形式を指定したもので,一般はこれを正規表現というのが自然です.SASのヘルプの解説をみると,パターンである正規表現を,m//演算子かs///演算子ではさんだパターン照合式「m/パターン/オプション」や「s/パターン/置換/オプション」自体を,Perl正規表現と呼んでいるようです.ここでは,それにあわせて狭義の正規表現のほうをパターンと呼ぶ事にします.

メタキャラクタ

パターンは,メタキャラクタと呼ばれる特別な意味をもった記号と,メタキャラクタでない普通の文字―こちらはその文字そのもの意味―の2種類の文字を組み合わせて作られます.たとえば,「PRX」で始まる文字列にマッチするパターンは,「PRX.*」とあらわせますが,この「PRX」が普通の文字で,「.*」が任意の0文字以上の文字列をあらわすメタキャラクタです.このパターンには,PRXMATCH,PRXCHANGEなどがマッチします.
メタキャラクタには次のものがあります.

メタキャラクタ機能
.任意の1文字にマッチ
\直後のメタキャラクタの文字をその文字そのものと扱う
[x-y]xからyまでのいずれかの1文字にマッチ
[^x-y]xからyまで以外のいずれか1文字にマッチ
^文字列の先頭にマッチ
$文字列の末尾にマッチ
*直前の文字の0個以上の並びの中で最長のもの.{0,}に同じ
+直前の文字の1個以上の並びの中で最長のもの.{1,}に同じ
?直前の文字の0個か1個のうち長いほう.{0,1}に同じ
{n}直前の文字のn個の並び
{n,}直前の文字のn個以上の並びの中で最長のもの
{n,m}直前の文字のn個以上m個以下の並びの中で最長のもの
*?直前の文字の0個以上の並びの中で最短のもの
+?直前の文字の1個以上の並びの中で最短のもの
??直前の文字の0個か1個のうち短いほう
{n,}?直前の文字のn個以上の並びの中で最短のもの
{n,m}?直前の文字のn個以上m個以下の並びの中で最短のもの
()パターンの一部分をグループ化する
xx|yyxxかyyのいずれかにマッチ
\w英数字かアンダスコアにマッチ.[A-Za-z0-9_]に同じ
\W英数字とアンダスコア以外にマッチ.[^A-Za-z0-9_]に同じ
\d数字にマッチ.[0-9]に同じ
\D数字以外にマッチ.[^0-9]に同じ
\s半角空白,改行,リターン,改頁,タブ(空白文字類)にマッチ.[ \n\r\f\t]に同じ
\S半角空白,改行,リターン,改頁,タブ以外にマッチ.[^ \n\r\f\t]に同じ
\b単語境界にマッチ
\B単語境界以外にマッチ
\tタブ
\n改行
\f改ページ
\rリターン

Perl正規表現を使用する関数

  • PRXMATCH関数
    PRXMATCH(Perl正規表現, 対象文字列)
    PRXMATCH(Perl正規表現ID, 対象文字列)
    対象文字列から,Perl正規表現にマッチした先頭位置を返す.Perl正規表現には,m//演算子か,s///演算子を指定する.
    data;
      html='<TD COLSPAN="2"><SMALL>■<B>注意事項</B></SMALL></TD>';
    
      pos=prxmatch('m/<.>/',html); /*1文字タグにマッチする<B>の先頭文字位置(26)を返す*/
      put pos=;
    
      prx='m/<\/.>/';/*正規表現は変数で与えてもよい*/
      pos=prxmatch(prx,html);
      put pos=;
    
      prxid=prxparse('m/<\/..>/');/*PRXPARSE関数でコンパイルし正規表現IDで処理*/
      pos=prxmatch(prxid,html);
      put pos=;
    
    run;
    /*
    pos=26 <B>の先頭文字位置
    pos=37 </B>の先頭文字位置
    pos=49 </TD>の先頭文字位置
     */
  • PRXCHANGE関数
    PRXCHANGE(Perl正規表現, 置換回数, 対象文字列)
    PRXCHANGE(Perl正規表現ID, 置換回数, 対象文字列)
    対象文字列から,Perl正規表現にマッチした部分を置換した文字列を返す.perl正規表現には,s///演算子を指定する.
    data;
      html='<TD COLSPAN="2"><SMALL>■<B>注意事項</B></SMALL></TD>';
    
      text=prxchange('s/<.*?>/./',-1,html); /*置換回数=-1:マッチしなくなるまで繰返し*/
      put text=;
    
      prx='s/.*<TD.*?>(.*)<\/TD>.*/$1/';/*正規表現は変数で与えてもよい*/
      text=prxchange(prx,1,html);
      put text=;
    
      /*PRXPARSE関数でコンパイルし正規表現IDで処理*/
      prxid=prxparse('s/<(\w+?) ([^<>]+?)>/<$1>/');/);
      text=prxchange(prxid,-1,html);
      put text=;
    
    run;
    /*
    text=..■.注意事項...                            タグを「.」に置換.
    text=<SMALL>■<B>注意事項</B></SMALL>            <TD>タグに囲まれた部分を取り出す
    text=<TD><SMALL>■<B>注意事項</B></SMALL></TD>   タグの属性指定部分を削除
     */
  • PRXPARSE関数
    PRXPARSE(Perl正規表現)
    Perl正規表現をコンパイルし,Perl正規表現IDを返す.後方参照を行うPRXPAREN関数と,PRXPOSN関数では,Perl正規表現IDを使わなければならないので,事前にこのPRXPARSE関数でPerl正規表現をコンパイルし,perl正規表現IDを適当な変数に取得しておく必要がある.
    (使用例は,他のperl正規表現の関数の例を参照のこと.)
  • PRXPAREN関数
    PRXPAREN(Perl正規表現ID)
    最後にPRXMATCH関数またはPRXCHANGE関数でパターンマッチングを行った結果において,
    マッチした部分パターン(括弧でグループ化されている正規表現の一部分)の最大の番号を返す.
    data;
      html='<TD COLSPAN="2"><SMALL>■<B>注意事項</B></SMALL></TD>';
    
      prxid=prxparse('m/(<A>)|(<B>)|(<C>)/');/*<A>か<B>か<C>のいずれか*/
      pos=prxmatch(prxid,html);
      paren=prxparen(prxid);
      put paren=;
    
    run;
    /*
    paren=2  2番目の括弧である部分パターン<B>がマッチ
     */
  • PRXPOSN関数
    PRXPOSN(Perl正規表現ID, 部分パターンの番号,対象文字列)
    最後にPRXMATCH関数またはPRXCHANGE関数でパターンマッチングを行った結果において,
    n番目の部分パターン(括弧でグループ化されている正規表現の一部分)にマッチしている文字列を返す.なお,対象文字列には,最後にパターンマッチングを行ったPRXMATCH関数またはPRXCHANGE関数で与えたものと同じ対象文字列を与えること.そうしないと期待した結果が得られないので注意しましょう.
    data;
      html='<TD COLSPAN="2"><SMALL>■<B>注意事項</B></SMALL></TD>';
    
      prxid=prxparse('m/<(\w+?) ([^<>]+?)>(.*?)<\/\1>/');
      pos=prxmatch(prxid,html);
      tag=prxposn(prxid,1,html);  /*1番目の括弧の部分パターン (\w+?) */
      attr=prxposn(prxid,2,html); /*2番目の括弧の部分パターン ([^<>]+?) */
      value=prxposn(prxid,3,html);/*3番目の括弧の部分パターン (.*?) */
    
      put tag= / attr= / value=;
    
    run;
    /*
    tag=TD
    attr=COLSPAN="2"
    value=<SMALL>■<B>注意事項</B></SMALL>
     */

よく使われるパターン例

注意すべきこと

正規表現を使う上で留意しておいたほうがよい点をいくつか上げておきます.

  • 正規表現のパターンマッチング機能は強力ですが,複雑なことができる分,照合に思わぬ時間がかかる場合もあります.正規表現を使わなくても容易に処理できる場合には,無理に使わないほうがいいかもしれません.
  • 無限ループにはいる場合もありますので,目的に類似したパターンをよく研究して記述するように努めましょう.
  • SAS9のPerl正規表現は,Perl5.6.1の正規表現がベースになっています.また,それにおいても,いくつかの機能はサポートされていないので,完全互換ではありません.