使人抓狂的代碼 - 萬能正則表達式.*陷阱

轉載請註明joymufeng,歡迎訪問 PlayScala社區(http://www.playscala.cn)

原文連接:http://www.playscala.cn/article/view?_id=10-59ee9f831d00000f0033a556html

先嚐試執行下面的代碼:java

println("play \n scala".matches(".*"))

你沒看錯,打印結果是false。正如鍵盤佈局同樣,這是因爲一個歷史問題致使的。早期的正則表達式工具是基於行處理文本的,因此.匹配的是除換行符之外的任意字符。大多數編程語言在設計正則表達式時沿用了這個傳統,可是提供一個選項用於開啓"點號匹配換行符"模式。正則表達式

Java提供了兩種方式開啓"點號匹配換行符"模式,第一種方式是在構建Pattern對象時指定匹配模式: 編程

val p = Pattern.compile(".*", Pattern.DOTALL) 
println(p.matcher("play\nscala").matches()) // true

另外一種方式是在正則表達式開始位置指定嵌入模式修飾符(embedded mode modifier),這也是一種比較通用的方式:api

println("play\nscala".matches("(?s).*")) // true

Pattern.DOTALL和(?s)是等價的。oracle

Java經常使用的匹配模式有如下幾種:編程語言

1) Pattern.DOTALL工具

啓用dotall模式。在dotall模式下,模式中的.匹配任意字符,包括換行符。在默認狀況下(即未啓用dotall模式),.不匹配換行符。等價於修飾符(?s)。佈局

val p = Pattern.compile(".*", Pattern.DOTALL)
val m = p.matcher("play\nscala")
println(m.matches())
// 輸出
true

2)Pattern.MULTILINEspa

啓用多行匹配模式。在多行匹配模式下,模式中的^和$將逐次匹配每一行的行首和行尾。在默認狀況下(即未啓用多行匹配模式),^和$將匹配整個字符串的首部和尾部。等價於修飾符(?m)。

val p = Pattern.compile("^.*$", Pattern.MULTILINE)
val m = p.matcher("play\nscala")
while (m.find()) {
  println("find: " + m.group(0))
}

// 輸出
find: play
find: scala

3) Pattern.UNIX_LINES

啓用Unix換行模式,使用"\n"標識每一行的末尾,等價於修飾符(?d)。

val p = Pattern.compile("^.*$", Pattern.UNIX_LINES | Pattern.MULTILINE)
val m = p.matcher("play\r\nscala")
while (m.find()) {
  println("find: " + m.group(0).length)
}
// 輸出
find: 5
find: 5

輸出的兩個結果長度都爲5,緣由是play末尾還有一個字符\r。

4)Pattern.CASE_INSENSITIVE

啓用大小寫不敏感匹配,等價於修飾符(?i)。

val p = Pattern.compile("^S.*A$", Pattern.CASE_INSENSITIVE)
val m = p.matcher("scala")
println(m.matches())
// 輸出
true

5)Pattern.LITERAL

啓用字面(literal)模式解析,模式中的元字符和轉義字符將按照普通字符解析。

val p = Pattern.compile(".*", Pattern.LITERAL)
val m = p.matcher("scala")
println(m.matches())
// 輸出
false

6)Pattern.COMMENTS

正則表達式中容許出現空白符(whitespace)和註解(comments),空白符會被忽略,以#開頭的註解行也將被忽略,等價於修飾符(?x);

val p = Pattern.compile(" .* ", Pattern.COMMENTS)
val m = p.matcher("scala")
println(m.matches())
// 輸出
true

注:有些編程語言(例如JavaScript)不支持嵌入模式修飾符(embedded mode modifier),這時可使用另外一種解決方案:

[\s\S]*

[\s]會匹配任意空白字符,[\S]而則會匹配[\s]不能匹配的任意字符。把這兩者組合起來構成[\s\S],這樣就會獲得一個包含全部字符的字符組,其中也包含了換行符。

參考:

正則表達式經典實例(第2版)

Java Pattern Doc

相關文章
相關標籤/搜索