一.regex(正規表示式):RegularExpressions(代替了StringTokenizer);字串處理利器;在unix流行,perl使用regex更牛。
主要用在字串匹配、尋找和替換。例如:匹配IP(範圍小於256)使用正規很好搞;從網頁中揪出大量email地址發送垃圾郵件;從網頁裡揪出連結。包含Matcher(用模式匹配字串後產生的結果)和pattern。
複製代碼代碼如下:
/*
* 告知此字串是否符合給定的正規表示式(也是一個字串)。
*/
System.out.println("abc".matches("..."));//每個"."表示一個字符
複製代碼代碼如下:
/*
* 把字串裡的所有數字替換成"-",普通方法要charAt逐一判斷;
* "//d"表示任一個數字或換成"[0-9]";
* "//D"表示任一個非數字或換成"[^0-9]"
*/
System.out.println("ab54564654sbg48746bshj".replaceAll("[0-9]", "-"));//每個"."表示一個字符
二、
複製代碼代碼如下:
/*
* compile將給定的正規表示式編譯到模式中(每次編譯需要費時間);{3}表示恰好三次。
* X{n} X,剛好n 次
* X{n,} X,至少n 次
* X{n,m} X,至少n 次,但不超過m 次
*/
Pattern p = Pattern.compile("[az]{3}");
Matcher m = p.matcher("ggs");//建立匹配給定輸入與此模式的匹配器。內部其實是創建了一個優先狀態的自動機(編譯原理)
//matcher和matches裡待匹配的字串其實是CharSequence(接口),不過String實作了這個接口,存在著多態性
System.out.println(m.matches());//若是"ggss"就不匹配了
//可一直接"ggs".matches("[az]{3}"),不過上面的有好處,至少效率高了,而且Pattern和Matcher提供了很多功能
三、在regex“. * +”中叫Meta Character;ctrl + shift + "/"表示註釋,換成"/"表示去掉註釋。
複製代碼代碼如下:
"a".matches(".");//true,"."表示任意一個字元,漢字也行
"aa".matches("aa");//true,也就是說普通字串也可以當作正規表示式
/*
* true,"*"表示0或多個字元,不過後面的要和第一個相同,
* 否則false,也就是判斷字串是否是單一字元組成的字串
*/
"aaaa".matches("a*");
"".matches("a*");//true
"aaa".matches("a?");//true,一次或0次
"".matches("a?");//true
"a".matches("a?");//true
"544848154564113".matches("//d{3,100}");//true
//這個是最簡單的IP判斷,不過若是超過255則判斷不出來
"192.168.0.aaa".matches("//d{1,3}//.//d{1,3}//.//d{1,3}//d{1,3}" );
"192".matches("[0-2][0-9][0-9]");
四、[abc]表示符合任意一個字元;[^abc]表示出了abc以外的其他字母(必須或字母,若是空串也回傳false)字元;[a-zA-Z]等價於"[az ]|[AZ]"是否為某個大小寫字母;[AZ&&[ABS]]表示大寫字母中取ABS中任一個。
複製代碼代碼如下:
//發現|和||沒差別,&和&&有差別,不知道這麼理解對不對
System.out.println("C".matches("[AZ&&[ABS]]"));//false
System.out.println("C".matches("[AZ&[ABS]]"));//true
System.out.println("A".matches("[AZ&&[ABS]]"));//true
System.out.println("A".matches("[AZ&[ABS]]"));//true
System.out.println("C".matches("[AZ|[ABS]]"));//true
System.out.println("C".matches("[AZ||[ABS]]"));//true
五、/w 單字字元:[a-zA-Z_0-9] 進行使用者名稱匹配時;/s 空白字元:[ /t/n/x0B/f/r]; /S 非空白字元:[^/s ] ;/W 非單字字元:[^/w] 。
複製代碼代碼如下:
" /n/t/r".matches("//s{4}");//true
" ".matches("//S");//false
"a_8".matches("//w{3}");//true
//“+”表示一次或多次
"abc888&^%".matches("[az]{1,3}//d+[&^#%]+");//true
/*
* 待匹配字元也只是一個反斜線,不過不可寫成"/"那麼和後面的"組合了,
* 前面的"無法匹配就會CE。
* 後面不可寫成"//",那麼會執行錯誤(編譯沒問題),必須寫成"////"
*/
System.out.println("//".matches("////"));//true
六、POSIX 字元類(僅US-ASCII)
複製代碼代碼如下:
/p{Lower} 小寫字母字元:[az] ;/p{Upper} 大寫字母字元:[AZ] ;/p{ASCII} 所有ASCII:[/x00-/x7F] ;/p{Alpha} 字母字元: [/p{Lower}/p{Upper}] ;/p{Digit} 十進位數字:[0-9] 。
七、邊界匹配器
^ 行的開頭$ 行的結尾/b 單字邊界/B 非單字邊界/A 輸入的開頭/G 上一個匹配的結尾/Z 輸入的結尾,僅用於最後的結束符號(如果有的話)
/z 輸入的結尾
複製代碼代碼如下:
"hello world".matches("^h.*");//^行的開頭
"hello world".matches(".*ld$");//$行的結尾
"hello world".matches("^h[az]{1,3}o//b.*");///b單字邊界
"helloworld".matches("^h[az]{1,3}o//b.*");
" /n".matches("^[//s&&[^//n]]*//n$");//判斷空白行,空白行開頭是空白符
八、還可以在find方法下使用m.start()和m.end()返回開始位置和結束位置的下一個;若是找不到則出錯。
複製代碼代碼如下:
Pattern p = Pattern.compile("//d{3,5}");
String s = "133-34444-333-00";
Matcher m = p.matcher(s);
m.matches();//matches匹配全部字串
m.reset();
/*
* 下面若是先呼叫了reset方法則輸出true,true,true,false.
* 否則倒數第二個find也輸出false。
* 原因如下:
* matches匹配到第一個"-"發現不匹配了,但是這四個字符已經被吃掉啦,再次匹配就從
* 34444開始了,第二個find從333,因為find匹配的是下一個子序列。
* reset方法讓matches吃掉的字串再吐出來。
* 綜上:matches和find之間要使用reset,因為二者互相影響
*
*/
m.find();
m.find();
m.find();//嘗試尋找與該模式相符的輸入序列的下一個子序列
m.find();
/*
* 嘗試將從區域開頭開始的輸入序列與該模式相符。
* Thinking in java的作者狠狠滴批評了這個方法,因為從字面看不出來到底從哪裡開始匹配。
* 下面全部是true,因為每次都從頭開始
*/
m.lookingAt();
m.lookingAt();
m.lookingAt();
m.lookingAt();
九、字串替換
複製代碼代碼如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestRegexReplacement {
public static void main(String[] args) {
Pattern p = Pattern.compile("java",Pattern.CASE_INSENSITIVE);//後面的參數是整形,表示“大小寫不敏感”
Matcher m = p.matcher("Java java hxsyl Ilovejava java JaVaAcmer");
while(m.find()) {
System.out.println(m.group());//m.group會輸出所有的java(忽略大小寫)
}
String s = m.replaceAll("Java");//String也有此方法
System.out.println(s);
m.reset();//一定要加,因為find和matcher互相影響
StringBuffer sb = new StringBuffer();
int i = 0;
/*
* 下面的方法是把找到的奇數個java換成“Java”,偶數個替換成"java"
*/
while(m.find()) {
i++;
//不能直接寫成i&1必須轉換成boolean
if((i&1)==1) {
m.appendReplacement(sb, "Java");
}else {
m.appendReplacement(sb, "java");
}
}
m.appendTail(sb);//把找到的最後一個java後邊的剩餘字串加上
System.out.println(sb);//不加reset的話只輸出了Acmer
}
}
十、分組
複製代碼代碼如下:
/*
* 分別加上小括號,不算最外邊的大括號,第一個左括號便是第一組
*/
Pattern p = Pattern.compile("(//d{3,5})([az]{2})");
String s = "123aaa-77878bb-646dd-00";
Matcher m = p.matcher(s);
while(m.find()) {
System.out.println(m.group());
System.out.println(m.group(1));//輸出每對符合的數字
System.out.println(m.group(2));//輸出每對符合的字母
}
十一、抓取網頁中的email
複製代碼代碼如下:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* 需要什麼養的方法的話先些方法名
* 然後ctrl + 1列出推薦,系統建立方法
*/
public class EmailSpider {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
BufferedReader br = new BufferedReader(new FileReader("F://regex.html"));
String line = "";
try {
while((line=br.readLine())!=null) {
solve(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void solve(String line) {
// TODO Auto-generated method stub
//正規表示式如果不滿足對應功能的話不會出錯,因為他是字串
Pattern p = Pattern.compile("[//w[.-]]+@[//w[.-]]+//.[//w]+");
Matcher m = p.matcher(line);
while(m.find()) {
System.out.println(m.group());
}
}
}
十二、代碼統計
複製代碼代碼如下:
View Code
/*
* 統計程式碼裡多少空行,註解行,程式行
* 實際上使用String裡的startsWith和endsWith也行.
* 若是專案經理用的話還要統計每行的字元數是否以{;結尾,防止偷懶
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CoderCount {
static long normalLines = 0;
static long commentLines = 0;
static long whiteLines = 0;
public static void main(String[] args) {
File f = new File("D://share//src");
File[] codeFiles = f.listFiles();
for(File child : codeFiles){
if(child.getName().matches(".*//.java$")) {
solve(child);
}
}
System.out.println("normalLines:" + normalLines);
System.out.println("commentLines:" + commentLines);
System.out.println("whiteLines:" + whiteLines);
}
private static void solve(File f) {
BufferedReader br = null;
boolean comment = false;
try {
br = new BufferedReader(new FileReader(f));
String line = "";
while((line = br.readLine()) != null) {
/*
* //有的註解行前面有一個tab
* 不可寫在readLine後
* 最後一行的話會空指針
*/
line = line.trim();
//readLine唸出字串後就把後面的換行去掉啦
if(line.matches("^[//s&&[^//n]]*$")) {
whiteLines ++;
} else if (line.startsWith("/*") && !line.endsWith("*/")) {
commentLines ++;
comment = true;
} else if (line.startsWith("/*") && line.endsWith("*/")) {
commentLines ++;
} else if (true == comment) {
commentLines ++;
if(line.endsWith("*/")) {
comment = false;
}
} else if (line.startsWith("//")) {
commentLines ++;
} else {
normalLines ++;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
br = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
十三、Quantifiers
包括? *+;預設全是Greedy,還有Reluctant和Possessive(獨佔性的)。
複製代碼代碼如下:
//加上分組是為了看得更清晰一些
Pattern p = Pattern.compile("(.{3,10})+[0-9]");
String s = "aaaa5bbbb6";//長度是10
Matcher m = p.matcher(s);
/*
* 現在輸出0-10,預設是Greedy,先吞進10個字符,發現不匹配,吐出來一個,發現匹配了;
* 若是Pattern.compile("(.{3,10}?)+[0-9]")則成了Reluctant,那麼是先吞進三個字符,發現不匹配,繼續吞入知道匹配,輸出0到5;
* 若是Pattern.compile("(.{3,10}++)+[0-9]")則是Possessive(獨佔式),也是先吞入10個字符,但是不向外吐,那麼就不匹配了,
* 這種方式主要用在需要高效率的地方(會有誤差)。
*/
if(m.find()) {
System.out.println(m.start() + "----" + m.end());
}else {
System.put.println("Not match!");
}
十四、補充(非捕獲組)
複製代碼代碼如下:
//非捕獲組的意思和字面相反,意思是若是符合則捕獲
Pattern p = Pattern.compile("(?=a).{3}");
/*
* 輸出a66,相當於要求以a開頭,也可以這麼寫Pattern.compile("[a].{2}");
* 若是Pattern.compile(".{3}(?!=a)")不是不以a結尾{2}[^a],而是下一個字元不是a(lookahead),輸出44a,66b,所以這種用法不常用;
* 若是Pattern.compile(".{3}(?=a)")則輸出444(因為?=a是lookahead),放在前面則包含在組內,後面則不包含在組內;
*
*
*/
String s = "444a66b";
Matcher m = p.matcher(s);
while(m.find()) {
System.out.println(m.group());
}
十五、Back Reference
複製代碼代碼如下:
Pattern p = Pattern.compile("(//d//d)//1");
/*
* 輸出true,//1表示和第一個組的一樣,若改成1213就不對了;
* 若是Pattern.compile("(//d(//d))//2")則需改成122才對
*
*/
String s = "1212";
Matcher m = p.matcher(s);
System.out.println(m.matches());
十六、flags的簡寫
"."是不符合換行的,記住CASE_INSENSITIVE就行了,簡寫「透過嵌入式標誌表達式(?i) 也可以啟用不區分大小寫的匹配」。