天天看點

爬蟲之正規表達式篇

最近再學習爬蟲抓取網站技術,但是遇到了一點小瓶頸,就是在進行爬蟲的時候需要一些正規表達式來進行模糊比對,而對于隻接觸過一點一點正規表達式的人肯定是不行的,是以花了一個下午上機課的時間學習,整理了下正規表達式的用法。

正規表達式

提問:

為什麼會用正規表達式(正規表達式的優點)?

典型的搜尋和替換操作要求您提供與預期的搜尋結果比對的确切文本。雖然這種技術對于對靜态文本執行簡單搜尋和替換任務可能已經足夠了,但它缺乏靈活性,若采用這種方法搜尋動态文本,即使不是不可能,至少也會變得很困難。

通過使用正規表達式,可以:

①測試字元串内的模式。

例如,可以測試輸入字元串,以檢視字元串内是否出現電話号碼模式或信用卡号碼模式。這稱為資料驗證。

②替換文本。

可以使用正規表達式來識别文檔中的特定文本,完全删除該文本或者用其他文本替換它。

③替換文本。

可以使用正規表達式來識别文檔中的特定文本,完全删除該文本或者用其他文本替換它。

正規表達式用到的主要包括下面三個java.util.regex 包裡面的類:

Pattern 類:

pattern 對象是一個正規表達式的編譯表示。Pattern 類沒有公共構造方法。要建立一個 Pattern 對象,你必須首先調用其公共靜态編譯方法,它傳回一個 Pattern 對象。該方法接受一個正規表達式作為它的第一個參數。

Matcher 類:

Matcher 對象是對輸入字元串進行解釋和比對操作的引擎。與Pattern 類一樣,Matcher 也沒有公共構造方法。你需要調用 Pattern 對象的 matcher 方法來獲得一個 Matcher 對象。

PatternSyntaxException:

PatternSyntaxException 是一個非強制異常類,它表示一個正規表達式模式中的文法錯誤。

下面進入代碼的演練分析:

添加測試類進行測試
	public class test2 {
	public static void main(String[] args) {
		String s = "abc123def";//需要比對的字元串
		testOne(s);
	}

	private static void testOne(String s){
		String pattern = "abc[0-9]+def$";//需要比對對應的正規表達式
		System.out.println(s.matches(pattern));、、傳回字元串和正規表達式所表達的所否一緻,一緻的話傳回一個true,否則傳回一個false
	}
}
           

1)為比對輸入字元串的開始位置。

[0-9]+比對多個數字, [0-9] 比對單個數字,+ 比對一個或者多個。
abc$比對字母 abc 并以 abc 結尾,$ 為比對輸入字元串的結束位置。

例如:令String s為:"123abc";
	String pattern為:"^[0-9]+abc$";     //代表比對以0到9到任意開頭(^[0-9])多個字元(+)并以abc結尾($);
	傳回值為:true;
           

2) \s:加在一個字元串後面,表示這個字元串後面可以有多個空格

\d+:表示比對一個或多個數字;

?:設定問号前面的括号内的内容是可選的;

.:比對"."

.XXX.:表示比對包含XXX的字元串

例如:令String s為:"5.1"   "5"   "2.21" 
     令String pattern為"^\\d+(\\.\\d+)?"	//代表比對以任意多個數字開頭(^\\d+)并可選擇的加入小數點和數字((\\.\\d+)?)   注意要是有小數點後面就必須有數字
	傳回值都為true
           

3)正規表達式捕獲數組操作

捕獲組是把多個字元當一個單獨單元進行處理的方法,它通過對括号内的字元分組來建立。

	例如,正規表達式 (dog) 建立了單一分組,組裡包含"d","o",和"g"。

	捕獲組是通過從左至右計算其開括号來編号。例如,在表達式((A)(B(C))),有四個這樣的組:

	((A)(B(C)))
	(A)
	(B(C))
	(C)
           

首先:\d 比對一個數字字元。等價于 [0-9]。

\D 比對一個非數字字元。等價于 [^0-9]。

捕獲組執行個體代碼:
	import java.util.regex.Matcher;
	import java.util.regex.Pattern;
 
	public class RegexMatches
	{
 	   	public static void main( String args[] ){
 	     // 按指定模式在字元串查找
      		String line = "This order was placed for QT3000! OK?";
      		String pattern = "(\\D*)(\\d+)(.*)"; 
      		// 建立 Pattern 對象
   	   Pattern r = Pattern.compile(pattern);
 		// 現在建立 matcher 對象
   		   Matcher m = r.matcher(line);
   	   if (m.find( )) {
     		System.out.println(m.group(0) );
      	 	  System.out.println(m.group(1) );
      	 	  System.out.println(m.group(2) );
      		   System.out.println(m.group(3) ); 
     	 } else {
       		  System.out.println("NO MATCH");
     	 }
   		}
	}
           
根據捕獲組的括号規則來運作後輸出的結果為:
	This order was placed for QT3000! OK?
	This order was placed for QT
	3000
	! OK?

           

正規表達式文法表:

在其他語言中,\ 表示:我想要在正規表達式中插入一個普通的(字面上的)反斜杠,請不要給它任何特殊的意義。

在 Java 中,\ 表示:我要插入一個正規表達式的反斜線,是以其後的字元具有特殊的意義。

是以,在其他的語言中(如Perl),一個反斜杠 \ 就足以具有轉義的作用,而在 Java 中正規表達式中則需要有兩個反斜杠才能被解析為其他語言中的轉義作用。也可以簡單的了解在 Java 的正規表達式中,兩個 \ 代表其他語言中的一個 \,這也就是為什麼表示一位數字的正規表達式是 \d,而表示一個普通的反斜杠是 \\。

字元 說明
\ 将下一字元标記為特殊字元、文本、反向引用或八進制轉義符。例如,“n"比對字元"n”。"\n"比對換行符。序列"\\“比對”\","\(“比對”("。
^ 比對輸入字元串開始的位置。如果設定了 RegExp 對象的 Multiline 屬性,^ 還會與"\n"或"\r"之後的位置比對。
$ 比對輸入字元串結尾的位置。如果設定了 RegExp 對象的 Multiline 屬性,$ 還會與"\n"或"\r"之前的位置比對。
* 零次或多次比對前面的字元或子表達式。例如,zo* 比對"z"和"zoo"。* 等效于 {0,}。
+ 一次或多次比對前面的字元或子表達式。例如,"zo+"與"zo"和"zoo"比對,但與"z"不比對。+ 等效于 {1,}。
? 零次或一次比對前面的字元或子表達式。例如,"do(es)?“比對"do"或"does"中的"do”。? 等效于 {0,1}。
{n} n 是非負整數。正好比對 n 次。例如,"o{2}"與"Bob"中的"o"不比對,但與"food"中的兩個"o"比對。
{n,} n 是非負整數。至少比對 n 次。例如,"o{2,}“不比對"Bob"中的"o”,而比對"foooood"中的所有 o。"o{1,}“等效于"o+”。"o{0,}“等效于"o*”。
{n,m} m 和 n 是非負整數,其中 n <= m。比對至少 n 次,至多 m 次。例如,"o{1,3}"比對"fooooood"中的頭三個 o。‘o{0,1}’ 等效于 ‘o?’。注意:您不能将空格插入逗号和數字之間。
[xyz] 字元集。比對包含的任一字元。例如,"[abc]“比對"plain"中的"a”。
[^xyz] 反向字元集。比對未包含的任何字元。例如,"[^abc]“比對"plain"中"p”,“l”,“i”,“n”。
[a-z] 字元範圍。比對指定範圍内的任何字元。例如,"[a-z]"比對"a"到"z"範圍内的任何小寫字母。
[^a-z] 反向範圍字元。比對不在指定的範圍内的任何字元。例如,"[^a-z]"比對任何不在"a"到"z"範圍内的任何字元。
\b 比對一個字邊界,即字與空格間的位置。例如,“er\b"比對"never"中的"er”,但不比對"verb"中的"er"。
\B 非字邊界比對。“er\B"比對"verb"中的"er”,但不比對"never"中的"er"。
\cx 比對 x 訓示的控制字元。例如,\cM 比對 Control-M 或回車符。x 的值必須在 A-Z 或 a-z 之間。如果不是這樣,則假定 c 就是"c"字元本身。
\d 數字字元比對。等效于 [0-9]。
\D 非數字字元比對。等效于 [^0-9]。
\f 換頁符比對。等效于 \x0c 和 \cL。
\n 換行符比對。等效于 \x0a 和 \cJ。
\s 比對任何空白字元,包括空格、制表符、換頁符等。與 [ \f\n\r\t\v] 等效。
\S 比對任何非空白字元。與 [^ \f\n\r\t\v] 等效。
\t 制表符比對。與 \x09 和 \cI 等效。
\v 垂直制表符比對。與 \x0b 和 \cK 等效。
\w 比對任何字類字元,包括下劃線。與"[A-Za-z0-9_]"等效。
\W 與任何非單詞字元比對。與"[^A-Za-z0-9_]"等效。
\xn 比對 n,此處的 n 是一個十六進制轉義碼。十六進制轉義碼必須正好是兩位數長。例如,"\x41"比對"A"。"\x041"與"\x04"&"1"等效。允許在正規表達式中使用 ASCII 代碼。
\num 比對 num,此處的 num 是一個正整數。到捕獲比對的反向引用。例如,"(.)\1"比對兩個連續的相同字元。
\nm 辨別一個八進制轉義碼或反向引用。如果 \nm 前面至少有 nm 個捕獲子表達式,那麼 nm 是反向引用。如果 \nm 前面至少有 n 個捕獲,則 n 是反向引用,後面跟有字元 m。如果兩種前面的情況都不存在,則 \nm 比對八進制值 nm,其中 n 和 m 是八進制數字 (0-7)。
\nml 當 n 是八進制數 (0-3),m 和 l 是八進制數 (0-7) 時,比對八進制轉義碼 nml。
\un 比對 n,其中 n 是以四位十六進制數表示的 Unicode 字元。例如,\u00A9 比對版權符号 (©)。

以下是幾個Matcher類的方法的介紹以及代碼:

索引方法:

方法 說明
public int start() 傳回以前比對的初始索引。
public int start(int group) 傳回在以前的比對操作期間,由給定組所捕獲的子序列的初始索引
public int end() 傳回最後比對字元之後的偏移量。
public int end(int group) 傳回在以前的比對操作期間,由給定組所捕獲子序列的最後字元之後的偏移量。

研究方法:

方法 說明
public boolean lookingAt() 嘗試将從區域開頭開始的輸入序列與該模式比對。
public boolean find() 嘗試查找與該模式比對的輸入序列的下一個子序列。
public boolean find(int start) 重置此比對器,然後嘗試查找比對該模式、從指定索引開始的輸入序列的下一個子序列。
public boolean matches() 嘗試将整個區域與模式比對。

替換方法

方法 說明
public Matcher appendReplacement(StringBuffer sb, String replacement) 實作非終端添加和替換步驟。
public StringBuffer appendTail(StringBuffer sb) 實作終端添加和替換步驟。
public String replaceAll(String replacement) 替換模式與給定替換字元串相比對的輸入序列的每個子序列。
public String replaceFirst(String replacement) 替換模式與給定替換字元串比對的輸入序列的第一個子序列。
public static String quoteReplacement(String s) 傳回指定字元串的字面替換字元串。這個方法傳回一個字元串,就像傳遞給Matcher類的appendReplacement 方法一個字面字元串一樣工作。

start和end方法

package Matcher;

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

public class Start和end方法 {
	private static final String REGEX = "\\bcat\\b";
    private static final String INPUT ="cat cat cat cattie cat";
    public static void main( String args[] ){
       Pattern p = Pattern.compile(REGEX);
       Matcher m = p.matcher(INPUT); // 擷取 matcher 對象
       int count = 0;
       while(m.find()) {
         count++;
         System.out.println("Match number "+count);
         System.out.println("start(): "+m.start());
         System.out.println("end(): "+m.end());
      }
   }
}
           

輸出資訊為:

Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
Match number 4
start(): 19
end(): 22
           

可以看出start會一直比對對應屬于的字元串,而end比對的是字元串的結尾位置

Start 方法傳回在以前的比對操作期間,由給定組所捕獲的子序列的初始索引,end 方法最後一個比對字元的索引加 1。

matches和lookingAt方法

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

public class matches和lookingAt方法 {
	private static final String REGEX = "foo";
    private static final String INPUT = "fooooooooooooooooo";
    private static final String INPUT2 = "ooooofoooooooooooo";
    private static Pattern pattern;
    private static Matcher matcher;
    private static Matcher matcher2;
 
    public static void main( String args[] ){
       pattern = Pattern.compile(REGEX);
       matcher = pattern.matcher(INPUT);
       matcher2 = pattern.matcher(INPUT2);
 
       System.out.println("Current REGEX is: "+REGEX);
       System.out.println("Current INPUT is: "+INPUT);
       System.out.println("Current INPUT2 is: "+INPUT2);
 
 
       System.out.println("lookingAt(): "+matcher.lookingAt());
       System.out.println("matches(): "+matcher.matches());
       System.out.println("lookingAt(): "+matcher2.lookingAt());
   }
}
           

以上代碼編譯後結果如下:

Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
Current INPUT2 is: ooooofoooooooooooo
lookingAt(): true
matches(): false
lookingAt(): false

           

matchs和lookingAt 方法都用來嘗試比對一個輸入序列模式。它們的不同是matches要求整個序列都比對 ,而lookingAt不要求。

lookingAt 方法雖然不需要整句都比對,但是需要從第一個字元開始比對。

這兩個方法經常在輸入字元串的開始使用。

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

public class matches和lookingAt方法 {
	private static final String REGEX = "foo";
    private static final String INPUT = "fooooooooooooooooo";
    private static final String INPUT2 = "ooooofoooooooooooo";
    private static Pattern pattern;
    private static Matcher matcher;
    private static Matcher matcher2;
 
    public static void main( String args[] ){
       pattern = Pattern.compile(REGEX);//使用foo來比對下面的字元串
       matcher = pattern.matcher(INPUT);
       matcher2 = pattern.matcher(INPUT2);
 
       System.out.println("Current REGEX is: "+REGEX);
       System.out.println("Current INPUT is: "+INPUT);
       System.out.println("Current INPUT2 is: "+INPUT2);
       
       System.out.println("lookingAt(): "+matcher.lookingAt());
       System.out.println("matches(): "+matcher.matches());
       System.out.println("lookingAt(): "+matcher2.lookingAt());
   }
}
           

replaceFirst 和 replaceAll 方法

replaceFirst 和 replaceAll 方法用來替換比對正規表達式的文本。不同的是,replaceFirst 替換首次比對,replaceAll 替換所有比對。

以下是運用這兩個方法的例子

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

public class replaceFirst和replaceAll方法 {
	private static String REGEX = "dog";
	private static String INPUT = "The dog says meow. " + "All dogs say meow.";
	private static String REPLACE = "cat";

	public static void main(String[] args) {
		Pattern p = Pattern.compile(REGEX);
		// get a matcher object
		Matcher m = p.matcher(INPUT);
		INPUT = m.replaceAll(REPLACE);
		System.out.println(INPUT);
	}
}
           

運作結果如下:

The cat says meow. All cats say meow.
           

appendReplacement 和 appendTail 方法

Matcher 類也提供了appendReplacement 和 appendTail 方法用于文本替換:

以下是運作的代碼:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexMatches
{
   private static String REGEX = "a*b";
   private static String INPUT = "aabfooaabfooabfoobkkk";
   private static String REPLACE = "-";
   public static void main(String[] args) {
      Pattern p = Pattern.compile(REGEX);
      // 擷取 matcher 對象
      Matcher m = p.matcher(INPUT);
      StringBuffer sb = new StringBuffer();
      while(m.find()){
         m.appendReplacement(sb,REPLACE);
      }
      m.appendTail(sb);
      System.out.println(sb.toString());
   }
}
           

運作結果如下:

-foo-foo-foo-kkk
           

以上文獻參考網址:http://www.runoob.com/java/java-regular-expressions.html