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

115 bài viết.
857 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
135 8
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 hơn 1 năm trước
135 8
White
109 14
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 gần 3 năm trước
109 14
White
86 10
(Ảnh) Mở đầu Chắc nhiều bạn đã nghe đến khái niệm oauth. Về cơ bản thì oauth là một phương thức chứng thực, mà nhờ đó một web service hay một ap...
huydx viết hơn 2 năm trước
86 10
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 2 năm trước
7 1
White
0 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 gần 2 năm trước
0 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 gần 2 năm trước
4 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

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


White
{{userFollowed ? 'Following' : 'Follow'}}
115 bài viết.
857 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á!