Memo về regex trong scala
Scala
50
White

huydx viết ngày 04/07/2015

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:

view raw README.md hosted with ❤ by GitHub
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))
}
}
}
view raw Macros.scala hosted with ❤ by GitHub
import Macros._
object Main extends App{
println("foo|bar".regex) // compile success !
println("[foo".regex) // compile error !
println("foo$".regex)
}
view raw Main.scala hosted with ❤ by GitHub

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

huydx

118 bài viết.
1028 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
163 15
Introduction (Link) là một cuộc thi ở Nhật, và cũng chỉ có riêng ở Nhật. Đây là một cuộc thi khá đặc trưng bởi sự thú vị của cách thi của nó, những...
huydx viết 2 năm trước
163 15
White
132 15
Happy programmer là gì nhỉ, chắc ai đọc xong title của bài post này cũng không hiểu ý mình định nói đến là gì :D. Đầu tiên với cá nhân mình thì hap...
huydx viết hơn 3 năm trước
132 15
White
124 14
Một ngày đẹp trời, bạn quyết định viết một dịch vụ web dự định sẽ làm thay đổi cả thế giới. Dịch vụ của bạn sẽ kết nối tất cả các thiết bị di động ...
huydx viết 29 ngày trước
124 14
Bài viết liên quan
White
7 1
Trong scala kí tự _ được dùng với khá nhiều mục đích .. không liên quan đến nhau. Tạm note lại cái đã khi nào có time sẽ quay lại viết cẩn thận sa...
huydx viết hơn 3 năm trước
7 1
White
1 0
(Bài viết hơi khó hiểu, dành cho bạn nào có hứng thú với type programming trong scala với các thư viện như shapeless chẳng hạn) Thông thường với m...
huydx viết hơn 2 năm trước
1 0
White
4 0
FSM (Finite State Machine) là design pattern được dùng nhiều khi viết game. Cốt lõi của FSM là: State(S) x Event(E) Actions(A), State(S') Có ngh...
Ngoc Dao viết hơn 2 năm trước
4 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
118 bài viết.
1028 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!