Java正則系列: (1)入門教程__Java

來源:互聯網
上載者:User

本文簡要介紹Java的Regex及其實現方式,並通過執行個體講解Regex的具體用法。 1. Regex 1.1. 簡介

Regex(Regular Expression), 簡稱 正則, 也翻譯為 正規式, 用來表示文本搜尋模式。英文縮寫是 regex(reg-ex).

搜尋模式(search pattern)可能多種多樣, 如, 單個字元(character), 特定字串(fixed string), 包含特殊含義的複雜運算式等等. 對於給定的字串, Regex可能匹配一到多次, 也可能一次都不匹配。

Regex一般用來尋找、編輯和替換文本(text), 本質上, text(文本) 和 string(字串) 是一回事。

用Regex來分析/修改文本的過程, 稱為: 應用於文本/字串的Regex 。Regex掃描字串的順序是從左至右. 每個字元都只能被匹配成功一次, 下次匹配掃描就會從後面開始。例如, Regex aba, 匹配字串 ababababa 時, 只會掃描到兩個匹配(aba_aba__)。 1.2. 樣本

最簡單的例子是字母串。例如, Regex Hello World 能匹配的就是字串 “Hello World”。 Regex中, 點號 .(dot,英文句號)屬於萬用字元, 點號匹配任意一個字元(character); 例如, “a” 或者 “1”; 當然, 預設情況下點號不能匹配換行 \n, 需要特殊標識指定才行。

下表列舉了一些簡單的Regex,和對應的匹配模式。

Regex Matches
this is text 完全符合 “this is text”
this\s+is\s+text 匹配的內容為: “this”, 加上1到多個空白符(whitespace character, 如空格,tab,換行等), 加上 “is”, 加上1到多個空白符, 再加上 “text”.
^\d+(\.\d+)? Regex以逸出字元 ^(小尖號)打頭, 表示這行必須以小尖號後面的字元模式開始, 才會達成匹配. \d+ 匹配1到多個數字. 英文問號 ? 表示可以出現 0~1次. \. 匹配的是字元 “.”, 小括弧(parentheses) 表示一個分組. 所以這個Regex可以匹配正整數或者小數,如: “5”, “66.6” 或者 “5.21” 等等.

說明,中文的全形空格( )字元不屬於空白字元(whitespace characters), 可以認為其屬於一個特殊的漢字。 1.3. 程式設計語言對Regex的支援

大多數程式設計語言都支援Regex, 如 Java、Perl, Groovy 等等。但各種語言的Regex寫法略有一些不同。 2. 預備知識

本教程要求讀者具備Java語言相關的基礎知識。

下面的一些樣本通過 JUnit 來驗證執行結果。如果不想使用JUnit, 也可以改寫相關代碼。關於JUnit的知識請參考 JUnit教程: http://www.vogella.com/tutorials/JUnit/article.html。 3. 文法規則

本章介紹各種正則元素的範本, 我們會先介紹什麼是元字元(meta character)。 3.1. 通用運算式簡介

Regex 說明
. 點號(.), 匹配任意一個字元
^regex 小尖號(^), 起始標識, 前面不能出現其他字元.
regex$ 貨幣符號($,dollar,美刀), 結束標識,後面不能再出現其他字元.
[abc] 字元組(set), 匹配 a 或 b 或 c.
[abc][vz] 字元組(set), 匹配 a 或 b 或 c,緊接著是 v 或 z.
[^abc] 如果小尖號(^, caret, 此處讀作 非) 出現在中括弧裡面的第一位, 則表示否定(negate). 這裡匹配: 除 a, b, c 之外的其他任一字元.
[a-d1-7] 範圍標記法: 匹配 a 到 d 之間的單個字元, 或者 1 到 7之間的單個字元, 整體只匹配單個字元, 而不是 d1 這種組合.
X|Z 匹配 X 或者 Z.
XZ 匹配XZ, X和Z必須按順序全部出現.
$ 判斷一行是否結束.
3.2. 元字元

下面這些是預定義的元字元(Meta characters), 可用於提取通用模式, 如 \d 可以代替 [0-9], 或者[0123456789]。

Regex 說明
\d 單個數字, 等價於 [0-9] 但更簡潔
\D 非數字, 等價於 [^0-9] 但更簡潔
\s 空白字元(whitespace), 等價於 [ \t\n\x0b\r\f]
\S 非空白字元, 等價於 [^\s]
\w 反斜線加上小寫w, 表示單個標識符,即字母數字底線, 等價於 [a-zA-Z_0-9]
\W 非單詞字元, 等價於 [^\w]
\S+ 匹配1到多個非空白字元
\b 匹配單詞外邊界(word boundary), 單詞字元指的是 [a-zA-Z0-9_]

這些元字元主要取自於對應單詞的英文首字母, 例如: digit(數字), space(空白), word (單詞), 以及 boundary(邊界)。對應的大寫字元則用來表示取反。 3.3. 量詞

量詞(Quantifier)用來指定某個元素可以出現的次數。?, *, + 和 {} 等符號定義了Regex的數量。

Regex 說明 樣本
* 0到多次, 等價於 {0,} X* 匹配0到多個連續的X, .* 則匹配任一字元串
+ 1到多次, 等價於 {1,} X+ 匹配1到多個連續的X
? 0到1次, 等價於 {0,1} X? 匹配0個,後者1個X
{n} 精確匹配 n 次 {} 前面序列出現的次數 \d{3} 匹配3位元字, .{10} 匹配任意10個字元.
{m, n} 出現 m 到 n 次, \d{1,4} 匹配至少1位元字,至多4位元字.
*? 在量詞後面加上 ?, 表示懶惰模式(reluctant quantifier). 從左至右慢慢掃描, 找到第一個滿足Regex的地方就暫停搜尋, 用來嘗試匹配最少的字串.
3.4. 分組和引用

可以對Regex進行分組(Grouping), 用圓括弧 () 括起來。這樣就可以對括弧內的整體使用量詞。

當然, 在進行替換的時候, 還可以對分組進行引用。也就是擷取的群組(captures the group)。反向參考(back reference) 指向匹配中該分組所對應的字串。進行替換時可以通過 $ 來引用。

使用 $ 來引用一個擷取的群組。例如 $1 表示第一組, $2 表示第二組, 以此類推, $0則表示整個正則所匹配的部分。

例如, 想要去掉單詞後面, 句號/逗號(point or comma)前面的空格。可以把句號/逗號寫入正則中, 然後原樣輸出到結果中即可。

// 去除單詞與 `.|,` 之間的空格String pattern = "(\\w)(\\s+)([\\.,])";System.out.println(EXAMPLE_TEST.replaceAll(pattern, "$1$3"));

提取 標籤的內容:

// 提取 <title> 標籤的內容pattern = "(?i)(<title.*?>)(.+?)()";String updated = EXAMPLE_TEST.replaceAll(pattern, "$2");
3.5. 環視

環視(lookaround), 分為順序環視(Lookahead)與逆序環視(lookbehind), 屬於零寬度斷言(zero-length assertion)。 類似於行起始標識(^)和結束標識($); 或者單詞邊界(\b)一類的位置標識。

順序否定環視(Negative look ahead), 用於在匹配的同時, 排除掉某些情形。也就是說其後面不能是符合某種特徵的字串。

順序否定環視(Negative look ahead) 使用 (?!pattern) 這種格式定義。例如, 下面的正則, 只匹配後面不是 b 字母的 “a” 字母。

a(?!b)

類似的, 順序環視(look ahead), 也叫順序肯定環視。 如,只匹配a字母, 但要求後面只能是 b 字母, 否則這個 a 就不符合需要:

a(?=b)

注意,環視 是一種向前/後尋找的文法: (?=exp), 會尋找後面位置的 exp; 所環視的內容卻不包含在Regex匹配中。

環視(lookaround)是一種進階技巧, 環視的部分不會匹配到結果之中, 但卻要求匹配的字串前面/後面具備環視部分的特徵。

如果將等號換成驚嘆號, 就是環視否定 (?!exp), 變成否定語義,也就是說尋找的位置的後面不能是exp。

逆序肯定環視, (?<=exp), 表示所在位置左側能夠匹配 exp

逆序否定環視, (?<!exp), 表示所在位置左側不能匹配 exp

詳情請參考: 正則應用之——逆序環視探索: http://blog.csdn.net/lxcnn/article/details/4954134

參考: 利用Regex排除特定字串 http://www.cnblogs.com/wangqiguo/archive/2012/05/08/2486548.html 3.6. Regex的模式

在Regex開頭可以指定模式修飾符(mode modifier)。還可以組合多種模式, 如 (?is)pattern。

(?i) Regex匹配時不區分大小寫。

(?s) 單行模式(single line mode), 使點號(.) 匹配所有字元, 包括換行(\n)。

(?m) 多行模式(multi-line mode), 使 小尖號(^,caret) 和 貨幣符號($, dollar) 匹配目標字串中每一行的開始和結束。 3.7. Java中的反斜線

在Java字串中, 反斜線(\, backslash) 是逸出字元, 有內建的含義。在原始碼層級, 需要使用兩個反斜線\\來表示一個反斜線字元。如果想定義的Regex是 \w, 在 .java 檔案源碼中就需要寫成 \\w。 如果想要匹配文本中的1個反斜線, 則源碼中需要寫4個反斜線 \\\\。 4. String類正則相關的方法 4.1. String 類重新定義了正則相關的方法

Java中 String 類內建了4個支援正則的方法, 即: matches(), split(), replaceFirst() 和replaceAll() 方法。 需要注意, replace() 是純字串替換, 不支援Regex。

這些方法並沒有對效能進行最佳化。稍後我們將討論最佳化過的類。

方法 說明
s.matches("regex") 判斷字串 s 是否能匹配正則 "regex". 只有整個字串匹配正則才返回 true .
s.split("regex") 用Regex "regex" 作為分隔字元來拆分字串, 返回結果是 String[] 數組. 注意 "regex" 對應的分隔字元並不包含在返回結果中.
s.replaceFirst("regex", "replacement") 替換第一個匹配 "regex" 的內容為 "replacement.
s.replaceAll("regex", "replacement") 將所有匹配 "regex" 的內容替換為 "replacement.

下面是對應的樣本。

package de.vogella.regex.test;public class RegexTestStrings {        public static final String EXAMPLE_TEST = "This is my small example "                        + "string which I'm going to " + "use for pattern matching.";        public static void main(String[] args) {                System.out.println(EXAMPLE_TEST.matches("\\w.*"));                String[] splitString = (EXAMPLE_TEST.split("\\s+"));                System.out.println(splitString.length);// should be 14                for (String string : splitString) {                        System.out.println(string);                }                // 將所有空白符(whitespace) 替換為 tab                System.out.println(EXAMPLE_TEST.replaceAll("\\s+", "\t"));        }}
4.2. 樣本

下面給出一些Regex的使用樣本。請參照注釋資訊。

If you want to test these examples, create for the Java project de.vogella.regex.string.

如果想測試這些樣本, 請將java檔案放到一個Java包下, 如 de.vogella.regex.string。

package de.vogella.regex.string;public class StringMatcher {        // 如果字串完全符合  "`true`", 則返回 true        public boolean isTrue(String s){                return s.matches("true");        }        // 如果字串完全符合  "`true`" 或 "`True`", 則返回 true        public boolean isTrueVersion2(String s){                return s.matches("[tT]rue");        }        // 如果字串完全符合  "`true`" 或 "`True`"        // 或 "`yes`" 或 "`Yes`", 則返回 true        public boolean isTrueOrYes(String s){                return s.matches("[tT]rue|[yY]es");        }        // 如果包含字串 "`true`", 則返回 true        public boolean containsTrue(String s){                return s.matches(".*true.*");        }        // 如果包含3個字母, 則返回 true        public boolean isThreeLetters(String s){                return s.matches("[a-zA-Z]{3}");                // 當然也等價於下面這種比較土的方式        // return s.matches("[a-Z][a-Z][a-Z]");        }        // 如果不以數字開頭, 則返回 true        public boolean isNoNumberAtBeginning(String s){        // 可能 "^\\D.*" 更好一點                return s.matches("^[^\\d].*");        }        // 如果包含了 `b` 之外的字元, 則返回 true        public boolean isIntersection(String s){                return s.matches("([\\w&&[^b]])*");        }        // 如果包含的某串數字小於300, 則返回 true        public boolean isLessThenThreeHundred(String s){                return s.matches("[^0-9]*[12]?[0-9]{1,2}[^0-9]*");        }}

And a small JUnit Test to validates the examples.

我們通過 JUnit 測試來驗證。

package de.vogella.regex.string;import org.junit.Before;import org.junit.Test;import static org.junit.Assert.assertFalse;import static org.junit.Assert.assertTrue;public class StringMatcherTest {        private StringMatcher m;        @Before        public void setup(){                m = new StringMatcher();        }        @Test        public void testIsTrue() {                assertTrue(m.isTrue("true"));                assertFalse(m.isTrue("true2"));                assertFalse(m.isTrue("True"));        }        @Test        public void testIsTrueVersion2() {                assertTrue(m.isTrueVersion2("true"));                assertFalse(m.isTrueVersion2("true2"));                assertTrue(m.isTrueVersion2("True"));;        }        @Test        public void testIsTrueOrYes() {                assertTrue(m.isTrueOrYes("true"));                assertTrue(m.isTrueOrYes("yes"));                assertTrue(m.isTrueOrYes("Yes"));                assertFalse(m.isTrueOrYes("no"));        }        @Test        public void testContainsTrue() {                assertTrue(m.containsTrue("thetruewithin"));        }        @Test        public void testIsThreeLetters() {                assertTrue(m.isThreeLetters("abc"));                assertFalse(m.isThreeLetters("abcd"));        }        @Test        public void testisNoNumberAtBeginning() {                assertTrue(m.isNoNumberAtBeginning("abc"));                assertFalse(m.isNoNumberAtBeginning("1abcd"));                assertTrue(m.isNoNumberAtBeginning("a1bcd"));                assertTrue(m.isNoNumberAtBeginning("asdfdsf"));        }        @Test        public void testisIntersection() {                assertTrue(m.isIntersection("1"));                assertFalse(m.isIntersection("abcksdfkdskfsdfdsf"));                assertTrue(m.isIntersection("skdskfjsmcnxmvjwque484242"));        }        @Test        public void testLessThenThreeHundred() {                assertTrue(m.isLessThenThreeHundred("288"));                assertFalse(m.isLessThenThreeHundred("3288"));                assertFalse(m.isLessThenThreeHundred("328 8"));                assertTrue(m.isLessThenThreeHundred("1"));                assertTrue(m.isLessThenThreeHundred("99"));                assertFalse(m.isLessThenThreeHundred("300"));        }}
5. Pattern與Matcher簡介

For advanced regular expressions the java.util.regex.Pattern and java.util.regex.Matcher classes are used.

要支援Regex的進階特性, 需要藉助 java.util.regex.Pattern 和 java.util.regex.Matcher 類。

首先建立/編譯 Pattern 對象, 用來定義Regex。對 Pattern 對象, 給定一個字串, 則產生一個對應的 Matcher 對象。通過 Matcher 對象就可以對 String 進行各種正則相關的操作。

package de.vogella.regex.test;import java.util.regex.Matcher;import java.util.regex.Pattern;public class RegexTestPatternMatcher {        public static final String EXAMPLE_TEST = "This is my small example string which I'm going to use for pattern matching.";        public static void main(String[] args) {                Pattern pattern = Pattern.compile("\\w+");                // 如需忽略大小寫, 可以使用:                // Pattern pattern = Pattern.compile("\\w+", Pattern.CASE_INSENSITIVE);                Matcher matcher = pattern.matcher(EXAMPLE_TEST);                // 尋找所有匹配的結果                while (matcher.find()) {                        System.out.print("Start index: " + matcher.start());                        System.out.print(" End index: " + matcher.end() + " ");                        System.out.println(matcher.group());                }                // 將空格替換為 tabs                Pattern replace = Pattern.compile("\\s+");                Matcher matcher2 = replace.matcher(EXAMPLE_TEST);                System.out.println(matcher2.replaceAll("\t"));        }}
6. Regex樣本

下面列出了常用的Regex使用情景。希望讀者根據實際情況進行適當的調整。 6.1 或(Or)

任務: 編寫Regex, 用來匹配包含單詞 “Joe” 或者 “Jim” , 或者兩者都包含的行。

建立 de.vogella.regex.eitheror 包和下面的類。

package de.vogella.regex.eitheror;import org.junit.Test;import static org.junit.Assert.assertFalse;import static org.junit.Assert.assertTrue;public class EitherOrCheck {        @Test        public void testSimpleTrue() {                String s = "humbapumpa jim";                assertTrue(s.matches(".*(jim|joe).*"));                s = "humbapumpa jom";                assertFalse(s.matches(".*(jim|joe).*"));                s = "humbaPumpa joe";                assertTrue(s.matches(".*(jim|joe).*"));                s = "humbapumpa joe jim";                assertTrue(s.matches(".*(jim|joe).*"));        }}
6.2. 匹配電話號碼

任務: 編寫Regex, 匹配各種電話號碼。

假設電話號碼(Phone number)的格式為 “7位連續的數字”; 或者是 “3位元字加空格/橫線, 再加上4位元字”。

package de.vogella.regex.phonenumber;import org.junit.Test;import static org.junit.Assert.assertFalse;import static org.junit.Assert.assertTrue;public class CheckPhone {        @Test        public void testSimpleTrue() {                String pattern = "\\d\\d\\d([,\\s])?\\d\\d\\d\\d";                String s= "1233323322";                assertFalse(s.matches(pattern));                s = "1233323";                assertTrue(s.matches(pattern));                s = "123 3323";                assertTrue(s.matches(pattern));        }}
6.3. 判斷特定數字範圍

以下樣本用來判斷文本中是否具有連續的3位元字。

建立 de.vogella.regex.numbermatch 包和下面的類。

package de.vogella.regex.numbermatch;import java.util.regex.Matcher;import java.util.regex.Pattern;import org.junit.Test;import static org.junit.Assert.assertFalse;import static org.junit.Assert.assertTrue;public class CheckNumber {        @Test        public void testSimpleTrue() {                String s= "1233";                assertTrue(test(s));                s= "0";                assertFalse(test(s));                s = "29 Kasdkf 2300 Kdsdf";                assertTrue(test(s));                s = "99900234";                assertTrue(test(s));        }        public static boolean test (String s){                Pattern pattern = Pattern.compile("\\d{3}");                Matcher matcher = pattern.matcher(s);                if (matcher.find()){                        return true;                }                return false;        }}
6.4. 校正超連結

假設需要從網頁中找出所有的有效連結。當然,需要排除 “javascript:” 和 “mailto:” 開頭的情況。

建立 de.vogella.regex.weblinks 包, 以及下面的類:

package de.vogella.regex.weblinks;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;public class LinkGetter {        private Pattern htmltag;        private Pattern link;        public LinkGetter() {
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.