まず、2箇所に出てくる実数部分にマッチするパターンを修正する。
質問者が使っている[1-9]+.?\d+
というパターンは、1
~9
の数字が1個以上連続し、その直後に.
が0個か1個あり、その直後に0
~9
の数字が1個以上連続する文字列にマッチする。これだと、
- 「0.5」や「10.5」のように、整数部に0を含み、小数部を持つ実数にマッチしない。
- マイナスの実数にマッチしない。
という問題がある。
そこで、
- 符号(
+
か-
)が0個または1個あり(省略可能ということ)
- その直後に
0
~9
の数字が1個以上連続し(「001」なども許容する)
- 「
.
(小数点)1個と、それに続く0
~9
の数字の1個以上の連続」が0個または1個ある(省略可能ということ)
と考えると、このパターンは([+-]?\d+(?:.\d+)?)
と書ける。これをJavaの文字列リテラルにすると、"([+-]?\\d+(?:\\.\\d+)?)"
となる。
なお、全体を()
で囲むのは、マッチした実数部分をキャプチャして取り出すため、内部で(?:...)
を使うのは、キャプチャすることを避けるため。
備考: このパターンの整数部分の\d+
を、「0
が1個だけか、1
~9
の数字1個とそれに続く0
~9
の数字の0個以上の連続」を意味するパターン(?:0|[1-9]\d*)
(文字列リテラルだと"(?:0|[1-9]\\d*)"
)に置換すれば、「001」などを除外できる。
次の問題は、区切りとなる空白2箇所を、質問文では文字列リテラル中に\s
と書いていることだ。\s
は正規表現パターンとしては正しいが、Javaの文字列リテラルとして書くには、\
をエスケープして\\s
と書く必要がある。
演算方法を示す「たす」の部分は、(たす|ひく|かける|わる)
のように書ける。
末尾の「は?」または「は?」の部分は、は[??]
と書ける。
なお、質問文のは?
だと、?
が正規表現のメタ文字として解釈されてしまうので、は\?
とエスケープする必要がある(文字列リテラルだと"は\\?"
)。もっとも、?
は文字クラス[...]
の内部ではメタ文字ではないので、文字クラスを使うならエスケープする必要はない。
以上をまとめ、文字列先頭と末尾にマッチするように^
と$
を付加すると、入力文字列を一気にパースするための正規表現パターンは、
regex
1^([+-]?\d+(?:.\d+)?)\s+(たす|ひく|かける|わる)\s+([+-]?\d+(?:.\d+)?)\s+(は[??])$
となり、
Java
1var pat = "^([+-]?\\d+(?:\\.\\d+)?)\\s+(たす|ひく|かける|わる)\\s+([+-]?\\d+(?:\\.\\d+)?)\\s+(は[??])$";
2var p = Pattern.compile(pat);
3var m = p.matcher(str);
とすれば、変数str
に格納済みの文字列に対するマッチを行ない、m.matches()
でパースできたかどうかを調べ、m.group(1)
とm.group(3)
で実数部分の文字列を得て、それらを実数に変換してからm.group(2)
で取得できる文字列に対応した演算を行なえばいいだろう。
一から書くなら、入力文字列全体を一気にパースせず、空白文字でsplit
して分割した文字列それぞれに対してマッチ処理を書いたほうが楽だろう。実数部分のパターンが重複して出てくることも避けられる。