JavaSE基礎:"頭疼"的正則表達式

正則表達式

1.正則是啥?

正則表達式: 定義一個搜索模式的字符串。正則表達式能夠用於搜索、編輯和操做文本.javascript

正則對文本的分析或修改過程爲: 首先正則表達式應用的是文本字符串(text/string),它會以定義的模式從左到右匹配文本,每一個源字符只匹配一次.html

請說人話:java

正則就是用有限的符號,表達無限的序列!正則表達式

說白了人就是懶,想少寫多作!segmentfault


正則表達式 匹配
this is text 精確匹配字符串 "this is text"
this\s+is\s+text 匹配單詞 "this" 後跟一個或多個空格字符,後跟詞 "is" 後跟一個或多個空格字符,後跟詞 "text"
^\d+(\.\d+)? ^ 定義模式必須匹配字符串的開始,d+ 匹配一個或多個數字,? 代表小括號內的語句是可選的,\. 匹配 ".",小括號表示分組。例如匹配:"5"、"1.5" 和 "2.21"

2.正則表達式的規則

2.1 常見匹配符號

正則表達式 描述
. 匹配全部單個字符,除了換行符(Linux 中換行是 n,Windows 中換行是 rn)
^regex 正則必須匹配字符串開頭
regex$ 正則必須匹配字符串結尾
[abc] 複選集定義,匹配字母 a 或 b 或 c
[abc][vz] 複選集定義,匹配字母 a 或 b 或 c,後面跟着 v 或 z
[^abc] 當插入符 ^ 在中括號中以第一個字符開始顯示,則表示否認模式。此模式匹配全部字符,除了 a 或 b 或 c
[a-d1-7] 範圍匹配,匹配字母 a 到 d 和數字從 1 到 7 之間,但不匹配 d1
XZ 匹配 X 後直接跟着 Z
X|Z 匹配 X 或 Z

這些符號必需要記住bash

2.2 元字符

元字符是一個預約義的字符。ide

正則表達式 描述
\d 匹配一個數字,是 [0-9] 的簡寫
\D 匹配一個非數字,是 [^0-9] 的簡寫
\s 匹配一個空格,是 [ \t\n\x0b\r\f] 的簡寫
\S 匹配一個非空格
\w 匹配一個單字字符(大小寫字母、數字、下劃線),是 [a-zA-Z_0-9] 的簡寫
\W 匹配一個非單字字符(除了大小寫字母、數字、下劃線以外的字符),等同於 [^\w]

2.3 限定字符

限定符定義了一個元素能夠發生的頻率。工具

正則表達式 描述 舉例
* 匹配 >=0 個,是 {0,} 的簡寫 X* 表示匹配零個或多個字母 X,.* 表示匹配任何字符串
+ 匹配 >=1 個,是 {1,} 的簡寫 X+ 表示匹配一個或多個字母 X
? 匹配 1 個或 0 個,是 {0,1} 的簡寫 X? 表示匹配 0 個或 1 個字母 X
{X} 只匹配 X 個字符 \d{3} 表示匹配 3 個數字,.{10} 表示匹配任何長度是 10 的字符串
{X,Y} 匹配 >=X 且 <=Y 個 \d{1,4} 表示匹配至少 1 個最多 4 個數字
*? 若是 ? 是限定符 *+?{} 後面的第一個字符,那麼表示非貪婪模式(儘量少的匹配字符),而不是默認的貪婪模式

2.4 分組和反向引用

小括號 () 能夠達到對正則表達式進行分組的效果。測試

模式分組後會在正則表達式中建立反向引用。反向引用會保存匹配模式分組的字符串片段,這使得咱們能夠獲取並使用這個字符串片段。ui

在以正則表達式替換字符串的語法中,是經過 $ 來引用分組的反向引用,$0 是匹配完整模式的字符串(注意在 JavaScript 中是用 $& 表示);$1 是第一個分組的反向引用;$2 是第二個分組的反向引用,以此類推。

package com.wuxianjiezh.demo.regex;

public class RegexTest {

    public static void main(String[] args) {
        // 去除單詞與 , 和 . 之間的空格
        String Str = "Hello , World .";
        String pattern = "(\\w)(\\s+)([.,])";
        // $0 匹配 `(\w)(\s+)([.,])` 結果爲 `o空格,` 和 `d空格.`
        // $1 匹配 `(\w)` 結果爲 `o` 和 `d`
        // $2 匹配 `(\s+)` 結果爲 `空格` 和 `空格`
        // $3 匹配 `([.,])` 結果爲 `,` 和 `.`
        System.out.println(Str.replaceAll(pattern, "$1$3")); // Hello, World.
    }
}
複製代碼

上面的例子中,咱們使用了 [.] 來匹配普通字符 . 而不須要使用 [\\.]。由於正則對於 [] 中的 .,會自動處理爲 [\.],即普通字符 . 進行匹配。

2.4.1 僅分組但無反向引用

當咱們在小括號 () 內的模式開頭加入 ?:,那麼表示這個模式僅分組,但不建立反向引用。

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "img.jpg";
        // 分組且建立反向引用
        Pattern pattern = Pattern.compile("(jpg|png)");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());//$0
            System.out.println(matcher.group(1));//$1
        }
    }
}
複製代碼

運行結果爲:

jpg

jpg

若源碼改成:

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "img.jpg";
        // 分組但不建立反向引用
        Pattern pattern = Pattern.compile("(?:jpg|png)");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
            System.out.println(matcher.group(1));
        }
    }
}
複製代碼

運行結果爲:

jpg Exception in thread "main" java.lang.IndexOutOfBoundsException: No group 1 at java.util.regex.Matcher.group(Matcher.java:538) at com.wuxianjiezh.regex.RegexTest.main(RegexTest.java:15)

2.4.2 分組的反向引用副本

Java 中能夠在小括號中使用 ? 將小括號中匹配的內容保存爲一個名字爲 name 的副本。

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "@wxj 你好啊";
        Pattern pattern = Pattern.compile("@(?<first>\\w+\\s)"); // 保存一個副本
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
            System.out.println(matcher.group(1));
            System.out.println(matcher.group("first"));
        }
    }
}
複製代碼

運行結果爲:

@wxj wxj wxj

2.5 否認先行斷言(Negative lookahead)

咱們能夠建立否認先行斷言模式的匹配,即某個字符串後面不包含另外一個字符串的匹配模式。

否認先行斷言模式經過 (?!pattern) 定義。好比,咱們匹配後面不是跟着 "b" 的 "a":

a(?!b)
複製代碼

2.6 指定正則表達式的模式

能夠在正則的開頭指定模式修飾符。

  • (?i) 使正則忽略大小寫。
  • (?s) 表示單行模式("single line mode")使正則的 . 匹配全部字符,包括換行符。
  • (?m) 表示多行模式("multi-line mode"),使正則的 ^$ 匹配字符串中每行的開始和結束。

2.7 Java 中的反斜槓

反斜槓 \ 在 Java 中表示轉義字符,這意味着 \ 在 Java 擁有預約義的含義。

這裏例舉兩個特別重要的用法:

  • 在匹配 .{[(?$^* 這些特殊字符時,須要在前面加上 \\,好比匹配 . 時,Java 中要寫爲 \\.,但對於正則表達式來講就是 \.
  • 在匹配 \ 時,Java 中要寫爲 \\\\,但對於正則表達式來講就是 \\

注意:Java 中的正則表達式字符串有兩層含義,首先 Java 字符串轉義出符合正則表達式語法的字符串,而後再由轉義後的正則表達式進行模式匹配。

2.8 易錯點示例

  • [jpg|png] 表明匹配 jpgpng 中的任意一個字符。
  • (jpg|png) 表明匹配 jpgpng

3.在字符串中使用正則表達式

3.1 內置的字符串正則處理方法

在 Java 中有四個內置的運行正則表達式的方法,分別是 matches()split())replaceFirst()replaceAll()。注意 replace() 方法不支持正則表達式。

方法 描述
s.matches("regex") 當僅且當正則匹配整個字符串時返回 true
s.split("regex") 按匹配的正則表達式切片字符串
s.replaceFirst("regex", "replacement") 替換首次匹配的字符串片斷
s.replaceAll("regex", "replacement") 替換全部匹配的字符
package com.wuxianjiezh.regex;

public class RegexTest {

    public static void main(String[] args) {
        System.out.println("wxj".matches("wxj"));
        System.out.println("----------");

        String[] array = "w x j".split("\\s");
        for (String item : array) {
            System.out.println(item);
        }
        System.out.println("----------");

        System.out.println("w x j".replaceFirst("\\s", "-"));
        System.out.println("----------");

        System.out.println("w x j".replaceAll("\\s", "-"));
    }
}
複製代碼

運行結果

true
----------
w
x
j
----------
w-x j
----------
w-x-j
複製代碼

4.模式和匹配

Java 中使用正則表達式須要用到兩個類,分別爲 java.util.regex.Patternjava.util.regex.Matcher

第一步,經過正則表達式建立模式對象 Pattern

第二步,經過模式對象 Pattern,根據指定字符串建立匹配對象 Matcher

第三步,經過匹配對象 Matcher,根據正則表達式操做字符串。

很是好的示例

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String text = "Hello Regex!";

        Pattern pattern = Pattern.compile("\\w+");
        // Java 中忽略大小寫,有兩種寫法:
        // Pattern pattern = Pattern.compile("\\w+", Pattern.CASE_INSENSITIVE);
        // Pattern pattern = Pattern.compile("(?i)\\w+"); // 推薦寫法
        Matcher matcher = pattern.matcher(text);
        // 遍例全部匹配的序列
        while (matcher.find()) {
            System.out.print("Start index: " + matcher.start());
            System.out.print(" End index: " + matcher.end() + " ");
            System.out.println(matcher.group());
        }
        // 建立第兩個模式,將空格替換爲 tab
        Pattern replace = Pattern.compile("\\s+");
        Matcher matcher2 = replace.matcher(text);
        System.out.println(matcher2.replaceAll("\t"));
    }
}
複製代碼

運行結果:

Start index: 0 End index: 5 Hello
Start index: 6 End index: 11 Regex
Hello    Regex!
複製代碼

5.若干個經常使用例子

5.1 中文的匹配

[\u4e00-\u9fa5]+ 表明匹配中文字。

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "閑人到人間";
        Pattern pattern = Pattern.compile("[\\u4e00-\\u9fa5]+");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }
}
複製代碼

運行結果:

閑人到人間
複製代碼

5.2 數字範圍的匹配

好比,匹配 1990 到 2017。

**注意:**這裏有個新手易範的錯誤,就是正則 [1990-2017],實際這個正則只匹配 01279 中的任一個字符。

正則表達式匹配數字範圍時,首先要肯定最大值與最小值,最後寫中間值。

正確的匹配方式:

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "1990\n2010\n2017";
        // 這裏應用了 (?m) 的多行匹配模式,只爲方便咱們測試輸出
        // "^1990$|^199[1-9]$|^20[0-1][0-6]$|^2017$" 爲判斷 1990-2017 正確的正則表達式
        Pattern pattern = Pattern.compile("(?m)^1990$|^199[1-9]$|^20[0-1][0-6]$|^2017$");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }
}
複製代碼

運行結果:

1990
2010
2017
複製代碼

5.3 img 標籤的匹配

好比,獲取圖片文件內容,這裏咱們考慮了一些不規範的 img 標籤寫法:

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "<img src='aaa.jpg' /><img src=bbb.png/><img src=\"ccc.png\"/>" +
                "<img src='ddd.exe'/><img src='eee.jpn'/>";
        // 這裏咱們考慮了一些不規範的 img 標籤寫法,好比:空格、引號
        Pattern pattern = Pattern.compile("<img\\s+src=(?:['\"])?(?<src>\\w+.(jpg|png))(?:['\"])?\\s*/>");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group("src"));
        }
    }
}
複製代碼

運行結果:

aaa.jpg
bbb.png
ccc.png
複製代碼

5.4 貪婪與非貪婪模式的匹配

好比,獲取 div 標籤中的文本內容:

package com.wuxianjiezh.regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main(String[] args) {
        String str = "<div>文章標題</div><div>發佈時間</div>";
        // 貪婪模式
        Pattern pattern = Pattern.compile("<div>(?<title>.+)</div>");
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group("title"));
        }

        System.out.println("--------------");

        // 非貪婪模式
        pattern = Pattern.compile("<div>(?<title>.+?)</div>");
        matcher = pattern.matcher(str);
        while (matcher.find()) {
            System.out.println(matcher.group("title"));
        }
    }
}
複製代碼

運行結果:

文章標題</div><div>發佈時間
--------------
文章標題
發佈時間
複製代碼

6.推薦詳細的自學教程

正則表達式30分鐘入門教程 https://deerchao.net/tutorials/regex/regex.htm

Java的正則表達式工具 http://www.regexplanet.com/advanced/java/index.html

正則表達式語法篇 https://yanhaijing.com/javascript/2017/08/06/regexp-syntax/

正則表達式語法詳解篇 https://blog.csdn.net/yaerfeng/article/details/28855587#reg

JavaScript正則表達式 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

7.附錄

問題1:關於空格

針對tab鍵帶來的多個空格問題,有時候咱們針對帶空格的一行數據要進行切割,若是有多個空格就會出現就會切割空格出現,咱們想把空格都去掉,因此須要用到某些方法。

解決方案: 利用正則表達式來匹配空格\\s+

首先利用split(「\s+」);方法來對字符串切割,儘量的匹配空格,這裏也挺有意思,由於空格數目不同,能夠動態變換匹配的空格數量,這個實現原理能夠看看底層原理,挺有意思。

String string="a b a a ";
for(String a:string.split("\\s+")){
    System.out.println(a);
}
複製代碼

問題2:[] {} () 的使用區別

  • () 是爲了提取匹配的字符串。表達式中有幾個()就有幾個相應的匹配字符串。(\s*)表示連續空格的字符串。
  • []是定義匹配的字符範圍。好比 [a-zA-Z0-9] 表示相應位置的字符要匹配英文字符和數字。[\s*]表示空格或者*號。
  • {}通常用來表示匹配的長度,好比 \s{3} 表示匹配三個空格,\s{1,3}表示匹配一到三個空格。
  • (0-9) 匹配 '0-9′ 自己。
  • [0-9]* 匹配數字(注意後面有 *,能夠爲空)
  • [0-9]+ 匹配數字(注意後面有 +,不能夠爲空){1-9} 寫法錯誤。
  • [0-9]{0,9} 表示長度爲 0 到 9 的數字字符串

問題3: ()和[]有本質的區別

()內的內容表示的是一個子表達式,()自己不匹配任何東西,也不限制匹配任何東西,只是把括號內的內容做爲同一個表達式來處理!

例如:(ab){1,3},就表示ab一塊兒連續出現最少1次,最多3次。若是沒有括號的話,ab{1,3},就表示a,後面緊跟的b出現最少1次,最多3次。另外,括號在匹配模式中也很重要。查看前面的介紹 []表示匹配的字符在[]中,而且只能出現一次,而且特殊字符寫在[]會被當成普通字符來匹配。例如[(a)],會匹配(、a、)、這三個字符。 因此() [] 不管是做用仍是表示的含義,都有天壤之別,沒什麼聯繫

文章出處:segmentfault.com/a/119000000…

相關文章
相關標籤/搜索