- inspired by macrocosm and inkytonik's blog post
Bạn có chắc chắn muốn xóa bài viết này không ?
Bạn có chắc chắn muốn xóa bình luận này không ?
Memo về regex trong scala
Kí tự Regex cơ bản
Về cơ bản thì các sử lý matching của scala.util.matching.Regex sẽ được "phó thác" (delegate) cho java Regex.
Bạn có thể tạo một object scala Regex thông qua 3 dấu quote và method r
giống như sau:
val foo = """(\d+)""".r
//foo : scala.util.matching.Regex = (\d+).*
Do sử dụng lại các kí tự regex của java nên chúng ta sẽ có các kí hiệu phổ biến sau:
Ký hiệu | Ý nghĩa |
---|---|
. |
Ký tự tuỳ ý |
[abc] |
1 ký tự a hoặc b hoặc c |
[^abc] |
1 ký tự nào đó khác a,b,c (ví dụ d) |
[A-Za-z] |
Từ A đến Z và từ a đến z |
\d |
Một chữ số |
\w |
Kí tự trống (space, tab, xuống dòng) |
\p{InBasicLatin} |
Một kí tự latin (symbol \p này chỉ có java mới có) |
\p{Hiragana} |
Một kí tự hiragana tiếng nhật (khá thú vị :D) |
\p{Katakana} |
Một kí tự katakana tiếng nhật (khá thú vị :D) |
? |
e?grep thì sẽ match với grep hoặc egrep |
? |
e?grep thì sẽ match với grep hoặc egrep |
* |
Có 0 hoặc 0 trở lên kí tự |
+ |
Có 1 hoặc 1 trở lên kí tự |
\{n} |
「a{2}」là 2 kí tự a |
Pattern matching bằng regex
Một điểm mà mình rất thích khi sử dụng scala regex là chúng ta có thể tận dụng pattern matching
thông qua hàm match và case class.
Bạn có thể hình dung khá dễ dàng bằng ví dụ dưới đây
val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
"2004-01-20" match {
case date(year, month, day) => s"$year was a good year for PLs."
}
"2004-01-20" match {
case date(year, _*) => s"$year was a good year for PLs."
}
Từ ví dụ trên chúng ta sẽ thấy:
- Sử dụng capture group trong regex sẽ giúp chúng ta lấy được các phần thông tin theo thứ tự
- Chúng ta có thể chỉ lấy một phần thông tin match được thôi không cần lấy hết, như trong ví dụ thứ 2
Ngoài ra còn một số hàm khá tiện dụng như: findFirstIn
val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15"
val firstDate = date findFirstIn dates getOrElse "No date found."
Hay là hàm replaceAllIn
val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15"
val redacted = date replaceAllIn (dates, "XXXX-XX-XX")
Hàm findAllIn
val hat = "hat[^a]+".r
val hathaway = "hathatthattthatttt"
val hats = (hat findAllIn hathaway).toList // List(hath, hattth)
val pos = (hat findAllMatchIn hathaway map (_.start)).toList // List(0, 7)
Hàm findAllMatchIn
for (words <- """\w+""".r findAllMatchIn "A simple example.") yield words.start
Một trick thú vị để phát hiện kí tự regex sai khi compile
Bạn muốn ở compile time có thể phát hiện các kí tự regex không đúng, bạn có thể sử dụng macro như sau:
import scala.reflect.macros.Context
import scala.util.matching.Regex
import java.util.regex.PatternSyntaxException
object Macros {
implicit class RegexContext(val c: String) {
def regex(): Regex = macro regexImpl
}
def regexImpl(c: Context)(): c.Expr[Regex] = {
import c.universe._
c.prefix.tree match {
case Apply(Select(_, _), List(Literal(Constant(str: String)))) =>
try{
str.r
}catch{
case e: PatternSyntaxException => c.abort(c.enclosingPosition, e.toString)
}
val Apply(fun, _) = reify(new Regex("")).tree
c.Expr[Regex](Apply.apply(fun, c.literal(str).tree :: Nil))
}
}
}
import Macros._
object Main extends App{
println("foo|bar".regex) // compile success !
println("[foo".regex) // compile error !
println("foo$".regex)
}
Trick này mình học từ sbt committer người nhật tên là yokota ở đây:
import scala.reflect.macros.Context | |
import scala.util.matching.Regex | |
import java.util.regex.PatternSyntaxException | |
object Macros { | |
implicit class RegexContext(val c: String) { | |
def regex(): Regex = macro regexImpl | |
} | |
def regexImpl(c: Context)(): c.Expr[Regex] = { | |
import c.universe._ | |
c.prefix.tree match { | |
case Apply(Select(_, _), List(Literal(Constant(str: String)))) => | |
try{ | |
str.r | |
}catch{ | |
case e: PatternSyntaxException => c.abort(c.enclosingPosition, e.toString) | |
} | |
val Apply(fun, _) = reify(new Regex("")).tree | |
c.Expr[Regex](Apply.apply(fun, c.literal(str).tree :: Nil)) | |
} | |
} | |
} |
import Macros._ | |
object Main extends App{ | |
println("foo|bar".regex) // compile success ! | |
println("[foo".regex) // compile error ! | |
println("foo$".regex) | |
} |







