CLOVER🍀

That was when it all began.

Scalaで正規表現

今まであまり触れてこなかったので、メモを兼ねて試してみることにしました。

普通にメソッド呼び出し

val targetString = "Hello 1234 World 5678"

val numberPattern = """\d+""".r
val alphaPattern = """[a-zA-Z]+""".r

// Regex#findFirstIn => Option[String]
numberPattern.findFirstIn(targetString)  // => Some(1234)
alphaPattern.findFirstIn(targetString)  // => Some(Hello)

// Regex#findFirstMatchIn => Option[Match]
numberPattern.findFirstMatchIn(targetString)  // => Some(1234)
alphaPattern.findFirstMatchIn(targetString)  // => Some(Hello)

// Regex#findAllIn => MatchIterator
numberPattern.findAllIn(targetString).mkString("[", ", ", "]")  // => [1234, 5678]
alphaPattern.findAllIn(targetString).mkString("[", ", ", "]")  // => [Hello, World]

// Regex#findPrefixOf => Option[String]
numberPattern.findPrefixOf(targetString)  // => None
alphaPattern.findPrefixOf(targetString)  // => Some(Hello)

// Regex#findPrefixMatchOf => Option[Match]
numberPattern.findPrefixMatchOf(targetString)  // => None
alphaPattern.findPrefixMatchOf(targetString)  // => Some(Hello)

// Regex#split => Array[String]
numberPattern.split(targetString).mkString("[", ", ", "]")  // => [Hello ,  World ]
alphaPattern.split(targetString).mkString("[", ", ", "]")  // => [,  1234 ,  5678]
""",""".r.split(targetString).mkString("[", ", ", "]")  // => [Hello 1234 World 5678]

// Regex#replaceFirstIn => String
numberPattern.replaceFirstIn(targetString, "number")  // => Hello number World 5678
alphaPattern.replaceFirstIn(targetString, "alpha")  // => alpha 1234 World 5678

// Regex#replaceAllIn(String, String) => String
numberPattern.replaceAllIn(targetString, "number")  // => Hello number World number
alphaPattern.replaceAllIn(targetString, "alpha")  // => alpha 1234 alpha 5678

// Regex#replaceAllIn(String, (Match => String)) => String
numberPattern.replaceAllIn(targetString, m => "number")  // => Hello number World number
alphaPattern.replaceAllIn(targetString, m => m.group(0))  // => Hello 1234 World 5678

// Regex#replaceSomeIn(String, (Match => Option[String])) => String
numberPattern.replaceSomeIn(targetString, m => Some("number"))  // => Hello number World number
alphaPattern.replaceSomeIn(targetString, m => None)  // => Hello 1234 World 5678

この使い方だと、パターン構築時にグループ化を使っても

val numberPattern = """(\d+)""".r
val alphaPattern = """([a-zA-Z]+)""".r

使わなくても

val numberPattern = """\d+""".r
val alphaPattern = """[a-zA-Z]+""".r

結果は変わりません。

名前付きグループ化

これは、素直にAPIリファレンスの例がわかりやすいかも。

import scala.util.matching.Regex
val datePattern = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day")
val text = "From 2011-07-15 to 2011-07-17"
val repl = datePattern replaceAllIn (text, m => m.group("month")+"/"+m.group("day"))
// => From 07/15 to 07/17

まあ、Matchオブジェクトが返却されたり、コールバック関数にMatchオブジェクトを取るものに使うんでしょうね。

import scala.util.matching.Regex

val targetString = "Hello 1234 World 5678"

val numberPattern = new Regex("""(\d+)(?:[^\d]+)(\d+)""", "firstNumber", "secondNumber")
val alphaPattern = new Regex("""([a-zA-Z]+)(?:[^a-zA-Z]+)([a-zA-Z])""", "firstAlpha", "secondAlpha")

// Regex#findFirstMatchIn => Option[Match]
numberPattern.findFirstMatchIn(targetString).foreach(m => println(m.group("secondNumber")))  // => 5678
alphaPattern.findFirstMatchIn(targetString).foreach(m => println(m.group("firstAlpha")))  // => Hello

// Regex#findPrefixMatchOf => Option[Match]
numberPattern.findPrefixMatchOf(targetString).foreach(m => println(m.group("secondNumber")))  // => 出力なし
alphaPattern.findPrefixMatchOf(targetString).foreach(m => println(m.group("firstAlpha")))  // => Hello

// Regex#replaceAllIn(String, (Match => String)) => String
numberPattern.replaceAllIn(targetString, m => m.group("firstNumber") + ":" + m.group("secondNumber"))  // => Hello 1234:5678
alphaPattern.replaceAllIn(targetString, m => m.group("firstAlpha") + ":" + m.group("secondAlpha"))  // => Hello:World 5678

// Regex#replaceSomeIn(String, (Match => Option[String])) => String
numberPattern.replaceSomeIn(targetString, m => Some(m.group("firstNumber")))  // => Hello 1234
alphaPattern.replaceSomeIn(targetString, m => None)  // => Hello 1234 World 5678

なんか、名前付きグループ化をreplaceAllInとかreplaceSomeInとかの中で使うと、置換はその1回で終わってしまうっぽい?APIリファレンスの例から考えると、名前を付けたパターン自体が繰り返しマッチしないと、複数回の呼び出しにはならないんでしょうね。

パターンマッチング

けっこう有名な割には、地味に癖があるような??

val targetString = "Hello 1234 World 5678"

val regex = """ ([a-zA-Z]+) """.r

regex.findAllIn(targetString).foreach(println)  // => World

targetString match {
  case regex(world) => println("Matched[%s]".format(world))
  case _ => println("Not Match")
}  // => Not Match

こんな感じで、文字列中の一部だけを書いたパターンだとキャプチャに失敗します。

パターンマッチングでキャプチャできるようにするには、文字列全体にマッチするパターンで書かないとダメっぽいですね。

val targetString = "Hello 1234 World 5678"

val regex = """([a-zA-Z]+) \d+ ([a-zA-Z]+) \d+""".r

regex.findAllIn(targetString).foreach(println)  // => Hello 1234 World 5678

targetString match {
  case regex(hello, world) => println("Matched[%s, %s]".format(hello, world))
  case _ => println("Not Match")
}  // => Matched[Hello, World]