天天看點

Java正則(1)— Pattern 詳解(一)

這個系列的文章我們使用以下的順序進行講解:

  1. Pattern

    詳解;
  2. Matcher

    詳解;
  3. 正規表達式文法詳解。

接下來先來介紹

Pattern

類。

在Java中,

java.util.regex

包定義了正規表達式使用到的相關類,其中最主要的兩個類為:

Pattern

Matcher

  • Pattern

    編譯正規表達式後建立一個比對模式;
  • Matcher

    使用

    Pattern

    執行個體提供的正規表達式對目标字元串進行比對,是真正影響搜尋的對象。。

另加一個新的例外類,PatternSyntaxException,當遇到不合法的搜尋模式時,會抛出例外。

Pattern 概述

聲明:public final class

Pattern

implements java.io.Serializable

Pattern 類有

final

修飾,可知他不能被子類繼承。

含義:模式類,正規表達式的編譯表示形式。

注意:此類的執行個體是不可變的,可供多個并發線程安全使用。

Pattern 比對模式(Pattern flags)

compile( )方法有一個版本,它需要一個控制正規表達式的比對行為的參數:

Pattern Pattern.compile(String regex, int flag)
           
flag 的取值範圍
字段 說明
Pattern.

UNIX_LINES

        unix行模式,大多數系統的行都是以

\n

結尾的,但是少數系統,比如Windows,卻是以

\r\n

組合來結尾的,啟用這個模式之後,将會隻以

\n

作為行結束符,這會影響到^、$和點号(點号比對換行符)。

        通過嵌入式标志表達式 (

?d

) 也可以啟用 Unix 行模式。
Pattern.

CASE_INSENSITIVE

        預設情況下,大小寫不敏感的比對隻适用于

US-ASCII

字元集。這個标志能讓表達式忽略大小寫進行比對。要想對

Unicode

字元進行大小不明感的比對,隻要将

UNICODE_CASE

與這個标志合起來就行了。

        通過嵌入式标志表達式(

?i

)也可以啟用不區分大小寫的比對。

        指定此标志可能對性能産生一些影響。

Pattern.

COMMENTS

⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢

        這種模式下,比對時會忽略(正規表達式裡的)空格字元(不是指表達式裡的”//s”,而是指表達式裡的空格,tab,回車之類)和注釋(從#開始,一直到這行結束)。

        通過嵌入式标志表達式(

?x

) 也可以啟用注釋模式。
Pattern.

MULTILINE

        預設情況下,輸入的字元串被看作是一行,即便是這一行中包好了換行符也被看作一行。當比對“^”到“$”之間的内容的時候,整個輸入被看成一個一行。啟用多行模式之後,包含換行符的輸入将被自動轉換成多行,然後進行比對。

        通過嵌入式标志表達式 (

?m

) 也可以啟用多行模式。
Pattern.

LITERAL

        啟用字面值解析模式。

        指定此标志後,指定模式的輸入字元串就會作為字面值字元序列來對待。輸入序列中的元字元或轉義序列不具有任何特殊意義。

        标志

CASE_INSENSITIVE

UNICODE_CASE

在與此标志一起使用時将對比對産生影響。其他标志都變得多餘了。

        不存在可以啟用字面值解析的嵌入式标志字元。

Pattern.

DOTALL

        在這種模式中,表達式

.

可以比對任何字元,包括行結束符。預設情況下,此表達式不比對行結束符。

        通過嵌入式标志表達式 (

?s

) 也可以啟用此種模式(s 是 “single-line” 模式的助記符,在 Perl 中也使用它)。
Pattern.

UNICODE_CASE

        在這個模式下,如果你還啟用了

CASE_INSENSITIVE

标志,那麼它會對Unicode字元進行大小寫不敏感的比對。預設情況下,大小寫不明感的比對隻适用于US-ASCII字元集。

        指定此标志可能對性能産生影響。

Pattern.

CANON_EQ

        當且僅當兩個字元的

正規分解(canonical decomposition)

都完全相同的情況下,才認定比對。比如用了這個标志之後,表達式

a/u030A

會比對

?

。預設情況下,不考慮

規範相等性(canonical equivalence)

        指定此标志可能對性能産生影響。

在這些标志裡面,

Pattern.CASE_INSENSITIVE

Pattern.MULTILINE

,以及

Pattern.COMMENTS

是最有用的(其中

Pattern.COMMENTS

還能幫我們把思路理清楚,并且/或者做文檔)。注意,你可以用在表達式裡插記号的方式來啟用絕大多數的模式。這些記号就在上面那張表的各個标志的下面。你希望模式從哪裡開始啟動,就在哪裡插記号。

可以用

OR

(

|

)運算符把這些标志配合使用。

代碼示例

多行模式:Pattern.

MULTILINE

示例

我測試了一下,也就是說如果沒有 MULTILINE 标志的話,

^

$

隻能比對輸入序列的開始和結束;否則,就可以比對輸入序列内部的行結束符。測試代碼如下:

import java.util.regex.*;

/**
 * 多行模式
 */
public class ReFlags_MULTILINE {

    public static void main(String[] args) {

        // 注意裡面的換行符
        String str = "hello world\r\n" + "hello java\r\n" + "hello java";

        System.out.println("===========比對字元串開頭(非多行模式)===========");
        Pattern p = Pattern.compile("^hello");
        Matcher m = p.matcher(str);
        while (m.find()) {
            System.out.println(m.group() + "   位置:[" + m.start() + "," + m.end() + "]");
        }

        System.out.println("===========比對字元串開頭(多行模式)===========");
        p = Pattern.compile("^hello", Pattern.MULTILINE);
        m = p.matcher(str);
        while (m.find()) {
            System.out.println(m.group() + "   位置:[" + m.start() + "," + m.end() + "]");
        }

        System.out.println("===========比對字元串結尾(非多行模式)===========");
        p = Pattern.compile("java$");
        m = p.matcher(str);
        while (m.find()) {
            System.out.println(m.group() + "   位置:[" + m.start() + "," + m.end() + "]");
        }

        System.out.println("===========比對字元串結尾(多行模式)===========");
        p = Pattern.compile("java$", Pattern.MULTILINE);
        m = p.matcher(str);
        while (m.find()) {
            System.out.println(m.group() + "   位置:[" + m.start() + "," + m.end() + "]");
        }
    }
}
           
===========比對字元串開頭(非多行模式)===========
hello   位置:[,]
===========比對字元串開頭(多行模式)===========
hello   位置:[,]
hello   位置:[,]
hello   位置:[,]
===========比對字元串結尾(非多行模式)===========
java   位置:[,]
===========比對字元串結尾(多行模式)===========
java   位置:[,]
java   位置:[,]
           
忽略大小寫:Pattern.

CASE_INSENSITIVE

示例

有的時候,需要進行忽略大小寫的比對。該例子實作比對攝氏溫度和華氏溫度,對于以C、c、F和f結尾的溫度值都能比對。

import java.util.regex.Pattern;

public class ReFlags_CASE_INSENSITIVE {

    public static void main(String[] args) {


        System.out.println("===========API忽略大小寫===========");
        String moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*[CF]";
        Pattern p = Pattern.compile(moneyRegex,Pattern.CASE_INSENSITIVE);

        System.out.println("-3.33c   " + p.matcher("-3.33c").matches());
        System.out.println("-3.33C   " + p.matcher("-3.33C").matches());


        System.out.println("===========不忽略大小寫===========");
        moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*[CF]";
        p = Pattern.compile(moneyRegex);

        System.out.println("-3.33c   " + p.matcher("-3.33c").matches());
        System.out.println("-3.33C   " + p.matcher("-3.33C").matches());


        System.out.println("===========正則内部忽略大小寫===========");
        moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*(?i)[CF]";
        p = Pattern.compile(moneyRegex);

        System.out.println("-3.33c   " + p.matcher("-3.33c").matches());
        System.out.println("-3.33C   " + p.matcher("-3.33C").matches());


        System.out.println("===========内部不忽略大小寫===========");
        moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*[CF]";
        p = Pattern.compile(moneyRegex);

        System.out.println("-3.33c   " + p.matcher("-3.33c").matches());
        System.out.println("-3.33C   " + p.matcher("-3.33C").matches());
    }
}
           
===========API忽略大小寫===========
-c   true
-C   true
===========不忽略大小寫===========
-c   false
-C   true
===========正則内部忽略大小寫===========
-c   true
-C   true
===========内部不忽略大小寫===========
-c   false
-C   true
           
啟用注釋:Pattern.

COMMENTS

示例

啟用注釋,開啟之後,正規表達式中的空格以及#号行将被忽略。

import java.util.regex.Pattern;

public class ReFlags_COMMENTS {

    public static void main(String[] args) {

        System.out.println("===========API啟用注釋===========");
        String comments = "    (\\d)+#this is comments.";
        Pattern p = Pattern.compile(comments, Pattern.COMMENTS);
        System.out.println("1234   " + p.matcher("1234").matches());

        System.out.println("===========不啟用注釋===========");
        comments = "    (\\d)+#this is comments.";
        p = Pattern.compile(comments);
        System.out.println("1234   " + p.matcher("1234").matches());

        System.out.println("===========正則啟用注釋===========");
        comments = "(?x)    (\\d)+#this is comments.";
        p = Pattern.compile(comments);
        System.out.println("1234   " + p.matcher("1234").matches());

        System.out.println("===========不啟用注釋===========");
        comments = "    (\\d)+#this is comments.";
        p = Pattern.compile(comments);
        System.out.println("1234   " + p.matcher("1234").matches());

    }
}
           
===========API啟用注釋===========
   true
===========不啟用注釋===========
   false
===========正則啟用注釋===========
   true
===========不啟用注釋===========
   false
           

可以看到,#号到行尾的注釋部分和前面的空白字元都被忽略了。正規表達式内置的啟用注釋為(?x)。

啟用 dotall 模式:Pattern.

DOTALL

示例

啟用dotall模式,一般情況下,點号(

.

)比對任意字元,但不比對換行符,啟用這個模式之後,點号還能比對換行符。

import java.util.regex.Pattern;

public class ReFlags_DOTALL {

    public static void main(String[] args) {

        System.out.println("===========API啟用DOTALL===========");
        String dotall = "<xml>(.)*</xml>";
        Pattern p = Pattern.compile(dotall, Pattern.DOTALL);
        System.out.println("<xml>\\r\\n</xml>   " + p.matcher("<xml>\r\n</xml>").matches());

        System.out.println("===========不啟用DOTALL===========");
        dotall = "<xml>(.)*</xml>";
        p = Pattern.compile(dotall);
        System.out.println("<xml>\\r\\n</xml>   " + p.matcher("<xml>\r\n</xml>").matches());

        System.out.println("===========正則啟用DOTALL===========");
        dotall = "(?s)<xml>(.)*</xml>";
        p = Pattern.compile(dotall);
        System.out.println("<xml>\\r\\n</xml>   " + p.matcher("<xml>\r\n</xml>").matches());

        System.out.println("===========不啟用DOTALL===========");
        dotall = "<xml>(.)*</xml>";
        p = Pattern.compile(dotall);
        System.out.println("<xml>\\r\\n</xml>   " + p.matcher("<xml>\r\n</xml>").matches());

    }
}
           
===========API啟用DOTALL===========
<xml>\r\n</xml>   true
===========不啟用DOTALL===========
<xml>\r\n</xml>   false
===========正則啟用DOTALL===========
<xml>\r\n</xml>   true
===========不啟用DOTALL===========
<xml>\r\n</xml>   false
           
平白字元模式 模式:Pattern.

LITERAL

示例

啟用這個模式之後,所有元字元、轉義字元都被看成普通的字元,不再具有其他意義。

import java.util.regex.Pattern;

public class ReFlags_LITERAL {

    public static void main(String[] args) {

        System.out.println(Pattern.compile("\\d", Pattern.LITERAL).matcher("\\d").matches());// true
        System.out.println(Pattern.compile("\\d", Pattern.LITERAL).matcher("2").matches());// false

        System.out.println(Pattern.compile("(\\d)+", Pattern.LITERAL).matcher("1234").matches());// false
        System.out.println(Pattern.compile("(\\d)+").matcher("1234").matches());// true

        System.out.println(Pattern.compile("(\\d){2,3}", Pattern.LITERAL).matcher("(\\d){2,3}").matches());// true
    }
}
           

繼續閱讀