正規表現を使って変数っぽいのを抜き出す。

というわけで、正規表現中途半端な理解の中、変数ぽいのを抜き出さないといけない仕事があったのでやってみた。参考にしたのはここ。

まず、要件としてあがっている変数とはこんな奴

  1. 「$」で始まる
  2. 英数字のみ使える(英語は大文字小文字どっちも可)
  3. , + - * / % ^ < > = & ( ) " ←こんな文字が来たら変数の終わり(スペースも、行末も含む)

これを実現するためのコードは以下のようになる。(エスケープ文字がみにくいね)

Pattern p = Pattern.compile("\\$[a-zA-Z0-9]+([,|+|\\-|*|/|%|\\^|<|>|=|&|(|)|\", ]+|$)");
Matcher m = p.matcher(formula); // formulaには式が入る ex) $abc = 10 * $x ・・・こんな感じの

// 変数を抜き出してSetに入れる
Set<String> valiableSet = new HashSet<String>();
while (m.find()) {
	valiableSet.add(formula.substring(m.start(0), m.start(1)));
}

まぁ、いまだにグループの使い方が怪しくて動かしながらやったら出来ましたって感じ。

この正規表現「\\$[a-zA-Z0-9]+([,|+|\\-|*|/|%|\\^|<|>|=|&|(|)|\", ]+|$)」は1つのグループが指定されていている。まるい括弧で囲まれたこの部分→「([,|+|\\-|*|/|%|\\^|<|>|=|&|(|)|\", ]+|$)」
これは、変数や定数の終わりとなる文字。これを踏まえて、formulaに以下の文字が指定された時を考えてみる。

String formula = "$abc = 10 * $x";

これを上記正規表現でマッチングすると以下の2つがマッチする。(whileの中でm.group()として取得した)

  • 「$abc+ 」
  • 「$x」

ただ、これだと変数の終わりとなる文字たちも一緒にくっついてきてしまう。(上記例だと「$abc+ 」の「+ 」の部分。)これを取り除きたいのでさっきのグループが出てくる。$abc+ の「+ 」の部分はグループ化しておいた「([,|+|\\-|*|/|%|\\^|<|>|=|&|(|)|\", ]+|$)」の部分にマッチしていることになる。なので以下のようにグループを指定することでマッチした文字の中の変数じゃない「+ 」の位置を知ることができる。

m.start(1)

Matcher#startの引数にグループを指定してマッチした文字が始まる位置を返す。グループ番号は1から始まる値らしい(今回は1つのグループを使っているのでそれを指定するにはstart(1)とする)。start(0)とやってるのはグループを指定しないのと同じstart()と同じ意味で、マッチした文字の位置を返す。次のm.start(1)ってのがグループ化した部分の文字が始まる位置を返す。つまり、

  • m.start(0) ・・・$abc+ が始まる位置を返し
  • m.start(1) ・・・+ が始まる位置を返す

そして、この2つを使って$abcという文字だけを抜き出すことができる。(while(m.find())でマッチした文字の数だけループしている)

while (m.find()) {
	valiableSet.add(formula.substring(m.start(0), m.start(1)));
}

ほうほう、なかなか面白いね。しかし、まだテスト少ししかしてないからちゃんとしなきゃ〜。