JavaRush /Java Blog /Random-TW /Java 中的正規表示式 (RegEx)

Java 中的正規表示式 (RegEx)

在 Random-TW 群組發布
正規表示式是程式設計師(即使是經驗豐富的程式設計師)經常推遲到以後再討論的話題。然而,大多數 Java 開發人員遲早會處理文字處理。最常見的是 - 在文字中進行搜尋操作和編輯。如果沒有正規表示式,與文字處理相關的高效且緊湊的程式碼簡直是不可想像的。所以別再拖延了,我們現在就來處理「常客」吧。這並不是一項艱鉅的任務。

RegEx 正規表示式是什麼?

事實上,正規表示式(Java 中的 RegEx)是一種在文字中搜尋字串的模式。在Java中,這種模式的初始表示總是一個字串,即String類別的物件。但是,並不是任何字串都可以編譯為正規表示式,只有那些遵循正規表示式編寫規則(語言規範中定義的語法)的字串才可以編譯為正規表示式。要編寫正規表示式,需要使用字母和數字字元以及元字元 - 在正規表示式語法中具有特殊含義的字元。例如:
String regex = "java"; // string template "java";
String regex = "\\d{3}"; // string template of three numeric characters;

在 Java 中建立正規表示式

要在 Java 中建立 RegEx,您需要遵循兩個簡單的步驟:
  1. 使用正規表示式語法將其寫為字串;
  2. 將此字串編譯為正規表示式;
在任何 Java 程式中使用正規表示式都是從建立類別物件開始的Pattern。為此,您需要呼叫類別中可用的兩個靜態方法之一compile。第一個方法採用一個參數 - 正規表示式的字串文字,第二個 - 加上另一個參數,用於開啟將模板與文字進行比較的模式:
public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
可能的參數值清單flags是在類別中定義的Pattern,並且可以作為靜態類別變數提供給我們。例如:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);//searching for matches with the pattern will be done case-insensitively.
本質上,該類別Pattern是一個正規表示式建構函數。在底層,該方法compile呼叫類別的私有建構函式Pattern來建立編譯視圖。實現這種創建模板實例的方法的目的是將其創建為不可變物件。建立時,會執行正規表示式的語法檢查。如果該行中有錯誤,則會產生異常PatternSyntaxException

正規表示式語法

正規表示式語法基於符號的使用<([{\^-=$!|]})?*+.>,符號可以與字母字元組合。根據他們的角色,他們可以分成幾組:
1. 用於匹配行邊界或文字的元字符
元字元 目的
^ 行首
$ 行結束
\b 字邊界
\B 沒有字數限制
\A 輸入開始
\G 上一場比賽結束
\Z 輸入結束
\z 輸入結束
2. 用於搜尋字元類別的元字符
元字元 目的
\d 數字符號
\D 非數字字符
\s 空格字符
\S 非空白字符
\w 字母數字字元或底線
\W 除字母、數字或下劃線之外的任何字符
任何字元
3. 用於搜尋文字編輯符號的元字符
元字元 目的
\t 製表符
\n 換行符
\r 回車符
\F 前往新頁面
\u0085 下一行字符
\u 2028 行分隔符
\u 2029 段落分隔符
4. 用於將字元分組的元字符
元字元 目的
[乙丙] 上述任何一項(a、b 或 c)
[^abc] 列出的以外的任何其他內容(非 a、b、c)
[a-zA-Z] 範圍合併(拉丁字元 a 到 z 不區分大小寫)
[廣告[mp]] 字串聯(a 到 d 和 m 到 p)
[az&&[def]] 符號的交集(符號 d、e、f)
[az&&[^bc]] 減去字元(字元 a、dz)
5. 表示字元數量的元符號-量詞。量詞總是位於一個字元或一組字元之後。
元字元 目的
一個或缺失
* 零次或多次
+ 一次或多次
{n} n次
{n,} n次或以上
{n,m} 不少於n次且不多於m次

貪婪量詞模式

量詞的一個特殊功能是能夠以不同的模式使用它們:貪婪、超貪婪和惰性。超貪婪模式+透過在量詞後面加上符號「 」開啟,惰性模式透過新增符號「 ?」開啟。例如:
"A.+a" // greedy mode
"A.++a" // over-greedy mode
"A.+?a" // lazy mode
使用此模板作為範例,讓我們嘗試了解量詞在不同模式下的工作方式。預設情況下,量詞以貪婪模式運行。這意味著它會在字串中查找最長的可能匹配。運行此程式碼的結果是:
public static void main(String[] args) {
    String text = "Egor Alla Alexander";
    Pattern pattern = Pattern.compile("A.+a");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(text.substring(matcher.start(), matcher.end()));
    }
}
我們將得到以下輸出: Alla Alexa 給定模式「 」的搜尋演算法А.+а按以下順序執行:
  1. 在給定的模式中,第一個字元是俄語字母字元АMatcher從位置零開始,將其與文字的每個字元進行匹配。在文本中的位置 0 處有一個符號Е,因此Matcher它會按順序遍歷文本中的字符,直到遇到與模式的匹配項。在我們的範例中,這是位置 5 處的符號。

    Java 中的正規表示式 - 2
  2. 找到與模式的第一個字元的匹配後,Matcher它會檢查與模式的第二個字元的匹配。在我們的例子中,這是符號“ .”,它代表任何字元。

    Java 中的正規表示式 - 3

    第六位是字母符號л。當然,它匹配“任何字元”模式。

  3. Matcher繼續檢查模式中的下一個字元。在我們的模板中,它是使用“ .+”量詞指定的。由於模式中「任意字元」的重複次數為一次或多次,因此Matcher只要滿足「任意字元」條件,它就會依次從字串中取出下一個字元並檢查其是否符合模式,在我們的範例中-直到行尾(從文本的第7 號位置到第18 號位置)。

    Java 中的正規表示式 - 4

    事實上,Matcher它把整條線都抓到了最後——這就是它的「貪婪」體現的地方。

  4. Matcher到達文字末尾並完成對模式的“ ”部分的檢查後А.+,Matcher 開始檢查模式的其餘部分 - 字母字元а。由於正向的文字已經結束,因此檢查發生在反向,從最後一個字元開始:

    Java 中的正規表示式 - 5
  5. Matcher「記住」模式「.+」到達文字結尾時的重複次數,因此它將重複次數減少一併檢查文字的模式,直到找到匹配: Java 中的正規表示式 - 6

超貪婪量詞模式

在超貪婪模式下,匹配器的工作方式與貪婪模式機制類似。不同之處在於,當您將文字抓取到行尾時,不會向後搜尋。也就是說,超貪婪模式的前三個階段將與貪婪模式類似。捕獲整個字串後,匹配器會新增模式的其餘部分並將其與捕獲的字串進行比較。在我們的範例中,當使用模式「А.++а」執行 main 方法時,將找不到匹配項。 Java 中的正規表示式 - 7

惰性量詞模式

  1. 在此模式下,在初始階段,與貪婪模式一樣,將尋求與模式的第一個字元的匹配:

    Java 中的正規表示式 - 8
  2. 接下來,它會尋找與模式中的下一個字元(任何字元)的匹配項:

    Java 中的正規表示式 - 9
  3. 與貪婪模式不同,惰性模式會搜尋文字中的最短匹配,因此在找到與模式的第二個字元(由點指定並與文字中第 6 號位置的字元匹配)的匹配後,將檢查文字是否Matcher與模式的其餘部分匹配- “ а”字元。

    Java 中的正規表示式 - 10
  4. 由於未找到與文字中的模式相符的內容(文本中的第 7 位置有符號“ ” л),Matcher因此在模式中添加另一個“任意字元”,因為它被指定為一次或多次,再次將圖案與第5 號到第8 號位置的文字進行比較:

    Java 中的正規表示式 - 11
  5. 在我們的例子中,找到了匹配項,但尚未到達文本末尾。因此,從位置 9 開始,檢查首先使用類似的演算法搜尋模式的第一個字符,然後重複直到文字結尾。

    Java 中的正規表示式 - 12
透過此方法,main當使用「А.+?а」範本時,我們將得到以下結果: Alla Alexa 從我們的範例中可以看出,當對相同範本使用不同的量詞模式時,我們得到了不同的結果。因此,在搜尋時需要考慮到此特點,根據想要的結果選擇所需的模式。

正規表示式中的轉義字符

由於 Java 中的正規表示式(或更準確地說其初始表示形式)是使用字串文字指定的,因此有必要考慮與字串文字相關的 Java 規範的規則。特別是,\Java 原始程式碼中字串文字中的反斜線字元「」被解釋為轉義字符,警告編譯器後面的字符是特殊字符,必須以特殊方式解釋。例如:
String s = "The root directory is \nWindows";//wrap Windows to a new line
String s = "The root directory is \u00A7Windows";//insert paragraph character before Windows
因此,在描述正規表示式並使用「\」字元(例如,對於元字元)的字串文字中,必須將其加倍,以便 Java 字節碼編譯器不會對其進行不同的解釋。例如:
String regex = "\\s"; // template for searching for space characters
String regex = "\"Windows\""; // pattern to search for the string "Windows"
如果我們打算將特殊字符用作“常規”字符,則還應該使用雙反斜杠字符來轉義特殊字符。例如:
String regex = "How\\?"; // template for searching the string "How?"

Pattern 類別的方法

這類Pattern還有其他處理正規表示式的方法: String pattern()– 傳回建立物件的正規表示式的原始字串表示形式Pattern
Pattern pattern = Pattern.compile("abc");
System.out.println(Pattern.pattern())//"abc"
static boolean matches(String regex, CharSequence input)– 讓您根據參數中傳遞的文字檢查在 regex 參數中傳遞的正規表示式input。傳回: true – 如果文字與模式相符; ——否則;例子:
System.out.println(Pattern.matches("A.+a","Alla"));//true
System.out.println(Pattern.matches("A.+a","Egor Alla Alexander"));//false
int flags()– 傳回flags已建立時設定的範本參數值,若未設定此參數則傳回0。例子:
Pattern pattern = Pattern.compile("abc");
System.out.println(pattern.flags());// 0
Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
System.out.println(pattern.flags());// 2
String[] split(CharSequence text, int limit)– 將作為參數傳遞的文字拆分為元素數組String。此參數limit決定在文字中搜尋的最大匹配數:
  • 當–執行匹配limit>0搜尋;limit-1
  • at limit<0– 搜尋文字中的所有符合項
  • when limit=0– 搜尋文字中的所有符合項,同時丟棄陣列末端的空白行;
例子:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    Pattern pattern = Pattern.compile("\\s");
    String[] strings = pattern.split(text,2);
    for (String s : strings) {
        System.out.println(s);
    }
    System.out.println("---------");
    String[] strings1 = pattern.split(text);
    for (String s : strings1) {
        System.out.println(s);
    }
}
控制台輸出: Egor Alla Anna -------- Egor Alla Anna下面我們將考慮 另一種用於建立物件的類別方法Matcher

匹配器類別方法

Matcher是一個類,從中建立物件來搜尋模式。Matcher– 這是一個“搜尋引擎”,一個正規表示式的“引擎”。為了進行搜索,他需要獲得兩件事:搜索模式和搜索“地址”。為了創建對象,Matcher類別中提供了以下方法Patternрublic Matcher matcher(CharSequence input) 作為參數,該方法採用將在其中執行搜尋的字元序列。這些是實作介面的類別的物件CharSequenceString您不僅可以傳遞, ,還可​​以傳遞StringBuffer, StringBuilder, Segmentand作為參數CharBuffer。搜尋模板是Pattern呼叫該方法的類別物件matcher。建立匹配器的範例:
Pattern p = Pattern.compile("a*b");// compiled the regular expression into a view
Matcher m = p.matcher("aaaaab");//created a search engine in the text “aaaaab” using the pattern "a*b"
現在,在「搜尋引擎」的幫助下,我們可以搜尋匹配項,找出匹配項在文字中的位置,並使用類別方法取代文字。該方法boolean find()使用該模式在文字中搜尋下一個匹配項。使用此方法和循環運算符,您可以根據事件模型分析整個文字(當事件發生時執行必要的操作 - 在文字中尋找匹配項)。例如,使用此類別的方法,int start()int end()可以確定文字中匹配的位置,並使用方法String replaceFirst(String replacement)String replaceAll(String replacement)文字中的匹配替換為另一個替換文字。例子:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    Pattern pattern = Pattern.compile("A.+?a");

    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        int start=matcher.start();
        int end=matcher.end();
        System.out.println("Match found" + text.substring(start,end) + " с "+ start + " By " + (end-1) + "position");
    }
    System.out.println(matcher.replaceFirst("Ira"));
    System.out.println(matcher.replaceAll("Olga"));
    System.out.println(text);
}
程式輸出: 從 5 到 8 個位置找到了匹配的 Alla A 從 10 到 13 個位置找到了安娜匹配 Egor Ira Anna Egor Olga Olga Egor Alla Anna 從示例中可以清楚地看出,這些方法replaceFirst創建了replaceAll一個新對象String- 一個字串,其中是來源文本,其中與模板的匹配項被替換為作為參數傳遞給方法的文本。此外,此方法replaceFirst僅取代第一個匹配項,以及replaceAll測試中的所有匹配項。原文保持不變。其他類別方法的使用Matcher以及正規表示式的範例可以在本系列文章中找到。處理文字時最常見的正規表示式運算來自類Pattern,並且Matcher內建於String. 這些是諸如splitmatchesreplaceFirst、之類的方法replaceAll。但事實上,「在幕後」他們使用PatternMatcher。因此,如果您需要在程式中替換文字或比較字串而不編寫不必要的程式碼,請使用String. 如果您需要高級功能,請考慮類別PatternMatcher.

結論

正規表示式在 Java 程式中使用與規則定義的模式相符的字串進行描述。當程式碼執行時,Java 將此字串重新編譯為類別對象Pattern,並使用該類別對像Matcher在文字中尋找匹配項。正如我在一開始所說的,正規表示式經常被放在一邊以供以後使用,被認為是一個困難的話題。然而,如果您了解語法、元字元、轉義的基礎知識,並研究正規表示式的範例,您會發現它們比乍看之下要簡單得多。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION