[Scala]正規表現でオプションを指定する

 正規表現を使っていると、大文字小文字の区別をしたくないときなど、オプションの指定が必要になることがあります。しかし残念なことに、Scalaではこのオプションの指定方法がサポートされていないようです。というわけで、今回はScalaのRegex(scala.util.matching.Regex)でオプションを指定する方法をご紹介します。

埋め込みフラグ表現

 Javaの正規表現エンジンには「埋め込みマッチフラグ」なるものがあるようです。Scalaは内部でJavaの正規表現を使用しているため、この埋め込みマッチフラグを利用することで、正規表現中にオプションを指定することができます。

val re = "(?i)(^[0-9a-z_]{1,15}$)".r

 このように、正規表現の先頭に「(?〜)」を付けることで、正規表現オプションと(ほぼ)同じ事ができます。上記の例は、大文字小文字の区別をしない(Case Insensitive)の例です。 以下のマッチフラグの対応関係をまとめます。

Javaのフラグ 埋め込みマッチフラグ 説明
CASE_INSENSITIVE (?i) 大文字と小文字を区別しないマッチングを有効にする
MULTILINE (?m) 複数行モードを有効にする
DOTALL (?s) DOTALLモードを有効にする(perlの単一行モード)
UNICODE_CASE (?u) Unicodeに準拠した大文字と小文字を区別しないマッチングを有効にする
CANON_EQ なし 正規等価を有効にする
UNIX_LINES (?d) Unixラインモードを有効にする
LITERAL なし パターンのリテラル構文解析を有効にする
COMMENTS (?x) パターン内で空白とコメントを使用できるようにする

 CANON_EQやLITERALなど、一部のフラグは使用できないようです。また、フラグの有効化は正規表現の途中からでも可能です。例えば下記のような正規表現があった場合、

val re = "he(?i)llo".r

「heLLO」や「hellO」などにマッチします。「Hello」にはマッチしません。また、途中からフラグを無効化したい場合は、「?」のあとに「-」を指定します。「(?i)」を無効化したいなら「(?-i)」となります。

Regexを継承して正規表現オプションに対応したクラスを定義する

 もう一つの方法です。こちらでは、ScalaのRegexが内部でJavaの正規表現エンジンを使っていることを利用して、オプションに対応した新しいクラスを定義する方法をご紹介します。

// JavaのPatternもインポート
import java.util.regex._
import scala.util.matching.Regex

class RegexWithFlags(regex: String, flags: Int) extends Regex(regex) {
    override val pattern = Pattern.compile(regex, flags)
}

object RegexWithFlags {
    val UnixLines       = Pattern.UNIX_LINES
    val CaseInsensitive = Pattern.CASE_INSENSITIVE
    val Comments        = Pattern.COMMENTS
    val MultiLine       = Pattern.MULTILINE
    val Literal         = Pattern.LITERAL
    val DotAll          = Pattern.DOTALL
    val UnicodeCase     = Pattern.UNICODE_CASE
    val CanonEq         = Pattern.CANON_EQ
}

 Regexのpatternをオーバーロードして、新しくオプションをとるクラスを定義します。オプションの値はJavaのPatternクラスが持つものをそのまま利用してもいいのですが、ここではScala風に記述できるように、RegexWithFlagsオブジェクトで定数として再定義しています。

 こんなことしなくても、普通に使うぶんにはJavaのPatternクラスを使っても問題ないとは思いますが、やっぱりmatch〜caseは使いたいのでRegexを継承してこのようなクラスを作っておくと便利ではないかと思います。

 参考にさせていただいたサイト: Scala の正規表現でマッチフラグを使うには - etc9

0 件のコメント :

コメントを投稿