正則表達式 深刻淺出2--從java API開始

前言

以前一直想要作一個本身的爬蟲,而後從nba數據相關的網上【虎撲,騰訊,官網等,要視網站是否支持】爬點數據寫數據分析和圖形化展現。雖然年輕的時候就實現過這個功能,可是當時直接借用了一個網上現成的jar包,而後在那個基礎上寫了一個很是簡陋的正則表達式來提取數據。此次打算本身用JAVA API寫一個爬蟲,裏面除了能讀取HTML或是JSON或是XML,還要可以相應的支持多線程【這個功能將最後完成】。
今天這篇博客重點講解java中和正則表達式相關的API爲後序程序的實現作一個鋪墊。後序的實現將在將來的博客3或4或5中展現,也會提供github源碼來供你們參考和指教。因此也歡迎你們關注我,以便得到後序的更新。java

進階的正則表達式

關於正則表達式的基本知識請參考個人博客正則表達式 深刻淺出1--你的符號我作主【持續更新中
在這裏,我須要再補充一些博客1中沒有說起可是必須瞭解的知識。(完整的正則表達式構造子列表請參考JAVA的API,在文章最後有給出傳送門)git


其它符號

博客1中,我將最經常使用的一些符號整理出來,這裏再說一些還須要瞭解的符號。github

字符正則表達式

\xhh 十六進制值爲0xhh的字符
\uhhhh 十六進制表示爲oxhhhh的Unicode字符segmentfault

字符類api

[abc[def]] 至關於合併操做 等價於[abcdef]
[a-z&&[hij]] 至關於交集操做,等價於[hij]
[a-z&&[^b-d]] 至關於減操做 等價於[ae-z]安全

邊界匹配符多線程

\b 詞的邊界,詞的邊界是指\w和\W之間的位置,它是一個定位符,並不表明任何具體的字符
\B 非詞的邊界,也就是是\w和\w 以及\W和\W之間的位置oracle

這裏新添的兩個定界符有點難理解。咱們已知\w是指詞字符[a-zA-Z0-9],而\W是指[^a-zA-Z0-9]。這裏舉個例子來講明bB都匹配了什麼。
假設咱們待匹配的字符串爲"hello world!\r\n"
若是調用方法String[] result = s.split("\\b");
那麼咱們會發現輸出爲["hello", "world", " ", "! rn"],也就是說咱們能夠將字符串當作是"_hello_ _world_!\r\n"其中_表明單詞分界處。
那麼若是_表明非單詞分界處,那麼上述句子能夠表示成"h_e_l_l_o w_o_r_l_d!_\r_\n"app

那麼,在大多數狀況下,咱們都會經過詞的邊界符來實現獲取而不是進行判斷。


量詞

量詞描述了一個模式吸取輸入文本的方式。量詞總共分爲三種,貪婪型、勉強型和佔有型。下面分別介紹這三種量詞以便更好的構建正則表達式。

貪婪型

貪婪型模式下的正則表達式會發現儘量多的匹配。也就是說,即使當前狀況已經知足了匹配,貪婪模式仍是會繼續測試下一個字符直至匹配失敗爲止。一旦匹配失敗,就開始回退直至找到合適的匹配。

在默認狀況下的通常正則表達式都是貪婪型的。

勉強型

在但願勉強匹配的正則表達式後面加?號便可進行勉強匹配。勉強匹配是指這個量詞知足模式所須要的最小字符數就馬上中止。

佔有型

目前,這種類型的量詞只在JAVA中才能夠用。佔有型不一樣於前面二者,它不會進行回溯。咱們知道一個正則表達式對應的字符串可能有多種。在貪婪型中,若是下一個匹配失敗,則放出已佔有字符回溯到上一個知足的情形繼續判斷。而勉強型則是多佔有一個字符繼續判斷。佔有型則不保存這些中間結果。一旦佔有則不會釋放。

下面舉一個例子來講明這些狀況:

輸入的字符串:<tr>info</tr>
貪婪模式正則表達式:<.*>
返回輸出:<tr>info</tr>
勉強模式正則表達式:<.*?>
返回輸出:<tr>
佔有模式正則表達式:<.*+>
返回輸出:<tr>info</tr>

Pattern和Matcher API

在Java中,和正則表達式最息息相關的兩個類就是PatternMatcher了。基本上全部正則表達式的底層實現都是經過Pattern和Matcher來實現的。好比說,咱們很是瞭解的String中的matches方法,實際上也是經過Pattern和Matcher的配合來實現的。
在這篇博客中,我將介紹重點的API,詳細的信息請各位自行參考JAVA DOC。

建立正則表達式並判斷字符流是否符合。

Pattern pattern = Pattern.compile("正則表達式");
    Matcher matcher = pattern.matcher("等待匹配的字符");//這裏能夠輸入任何繼承了CharSequence的類
    matcher.matches();//返回一個boolean值說明是否匹配

這裏須要注意的是,Pattern和Matcher均不容許經過構造器新建一個對象。不只如此,Pattern類是相對而言線程安全的,而Matcher類不是如此。

Pattern類

構造器

static Pattern compile(String regex)
    static Pattern compile(String regex, int flag);

第二個構造pattern的方法中多了一個flag參數,這個參數容許咱們定義pattern的模式,這裏講幾個比較重要的模式:

Pattern.CASE_INSENSITIVE: 等價於正則表達式中的?i,是的匹配能夠大小寫不敏感
Pattern.COMMENTS : 等價於正則表達式中的?x,會忽略空格符和以#開頭到行末的註釋
Pattern.MULTILINE : 等價於?m,使得^和&符號能夠匹配一行的始末,而不只僅是整個字符串的始末

若是但願有多個Pattern,能夠輸入Pattern.compile("正則表達式", Pattern1 | Pattern2 | Pattern3)

分割
Pattern本身也定義了split方法。它會根據以前compile的正則表達式進行分割並返回String[],limit值是指分割出的String[]的size不會超過這個limit值。

String[] split(CharSequences c, int limit)
String[] split(CharSequences c)

分組
在這裏還須要在講解一個分組的概念以便爲後面作鋪墊。正則表達式的分組是指將一對括號中的表達式劃爲一個分組。所以形如A(B(CD)(E))的正則表達式一共有四個分組{A(B(CD)(E)),B(CD)(E),(CD),(E)},其中第0個分組默認爲完整的正則表達式。

Matcher類

查找

int groupCount() //返回除了第0組的總分組數
     boolean find() //從當前下標開始匹配,若是存在知足正則表達式的值,則返回true
     boolean find(int i)//從下標i開始匹配,若是存在知足正則表達式的值,返回true
     String group() //返回前一次匹配的第0個分組的內容
     String group(int i)//返回前一次匹配的第i個分組的內容
     int start() //返回上一次匹配成功的內容的起始下標
     int end() //返回上一次匹配成功的內容的終止下標+1

舉個栗子

Pattern p = Pattern.compile("<.*?>");
    Matcher matcher = p.matcher("<tr>data</tr>");
    while(matcher.find()){
        System.out.println(matcher.group());
        //前後輸出<tr> </tr>
    }

替換

void replaceFirst(String replacement);
    void replaceAll(String replacement);
    void appendReplacement(StringBuffer s, String replacement);
    void appendTail(StringBuffer s);

這裏講一下appendReplacement和appendTail方法。appendReplacement容許開發者在替換的過程當中針對將被替換的內容進行一些動態的操做。這個方法將逐步推動的向前替換,並將替換的結果添加到輸入的s中。除此之外,對於沒有被掃描到的部分,能夠經過appendTail方法添加到輸入的s中。

例子:

Pattern p = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher("hello world one hello world 2");
    StringBuffer s = new StringBuffer();
    while(m.find()){
        m.appendReplacement(s, m.group().toUpperCase());
        System.out.println(s);
    }
    m.appendTail(s);
    System.out.println(s);

輸出爲:

clipboard.png

小結

其實正則表達式的適用範圍很是廣,除了和CharSequence配合使用以外,還能夠和JAVA I/O如Scanner類,InputReader類等聯合使用。核心仍是在於Pattern類和Matcher類的組合使用。

References

JAVA API

相關文章
相關標籤/搜索