一 字符串
1、不可變String
String對象是不可變的,查看JDK文檔你就會發現,String類中每一個看起來會修改String值的方法,實際上都是創建一個全新的String對象,以包含修改后的字符串內容。而最初的String對象則沒有改變。
看看下面的代碼:
public class Immutable { public static String upcase(String s) { return s.toUpperCase(); } public static void main(String[] args) { String q = "howdy"; System.out.println(q); String qq = upcase(q); System.out.println(qq); System.out.println(q); } }
輸出如下:
howdy HOWDY howdy
當把q傳遞給upcase()方法時,實際傳遞的是引用的一個拷貝。其實,每當把String對象作為方法的參數時,都會復制一份引用,而該引用所指的對象其實一直待在單一的物理位置上,從未改變。
回到upcase()的定義,傳入其中的引用有了名字s,只有upcase()運行的時候,局部引用s才存在。一旦upcase()運行結束,s就消失了。當然了,upcase()的返回值,其實只是最終結果的引用。這足以說明,upcase()返回的引用已經指向了一個新的對象,而原本的q則還在原地。
String的這種行為其實正是我們想要的。例如:
String s = "asdf"; String x = Immutable.upcase(s);
難道我們真的希望upcase()改變其參數嗎?對於一個方法而言,參數是為該方法提供信息的,而不是想讓該方法改變自己的。
2、StringBuilder
String的不可變性會帶來一定的效率問題。以下面一段代碼為例:
String str="abc"; System.out.println(str); str=str+"de"; System.out.println(str);
如果運行這段代碼會發現先輸出“abc”,然后又輸出“abcde”,好像是str這個對象被更改了,其實,這只是一種假象罷了,JVM對於這幾行代碼是這樣處理的,首先創建一個String對象str,並把“abc”賦值給str,然后在第三行中,其實JVM又創建了一個新的對象也名為str,然后再把原來的str的值和“de”加起來再賦值給新的str,而原來的str就會被JVM的垃圾回收機制(GC)給回收掉了,所以,str實際上並沒有被更改,也就是前面說的String對象一旦創建之后就不可更改了。所以,Java中對String對象進行的操作實際上是一個不斷創建新的對象並且將舊的對象回收的一個過程,所以執行速度很慢。
為此Java引入了StringBuilder,StringBuilder對象是變量,對變量進行操作就是直接對該對象進行更改,而不進行創建和回收的操作,所以速度要比String快很多。
另外,有時候我們會這樣對字符串進行賦值:
String str="abc"+"de"; StringBuilder stringBuilder=new StringBuilder().append("abc").append("de"); System.out.println(str); System.out.println(stringBuilder.toString());
這樣輸出結果也是“abcde”和“abcde”,但是String的速度卻比StringBuilder的反應速度要快很多,這是因為第1行中的操作和String str="abcde";是完全一樣的,所以會很快,而如果寫成下面這種形式:
String str1="abc"; String str2="de"; String str=str1+str2;
那么JVM就會像上面說的那樣,不斷的創建、回收對象來進行這個操作了。速度就會很慢。
總結一下:
- String:適用於少量的字符串操作的情況;
- StringBuilder:適用於單線程下在字符緩沖區進行大量操作的情況;
- StringBuffer:適用多線程下在字符緩沖區進行大量操作的情況;(后面會講)
3、String對象的基本方法
- 構造器 :默認版本、String、StringBuilder、StringBuffer、char數組、byte數組;
- length():字符的個數;
- charAt():獲取String中指定索引位置上的char;
- toCharArray():生成一個char[],包含String中的所有字符;
- ....
二 格式化輸出
Java SE5推出了C語言中printf()風格的格式化輸出這一功能。
1、System.out.format()
JAVA SE5引入的format()方法可用於PrintStream或PrintWriter對象,其中也包括System.out對象。format()方法模仿自C的printf()。如果你比較習慣使用printf()的話,你也可以使用System.out.printf()方法。
下面是一個簡單的示例:
public class SimpleFormat { public static void main(String[] args) { int x = 5; double y = 5.332542; System.out.println("Row 1: [" + x + " " + y + "]"); //原來方式 //格式化輸出方式 System.out.format("Row 1: [%d %f]\n", x,y); System.out.printf("Row 1: [%d %f]\n", x,y); } }
輸出如下:
Row 1: [5 5.332542] Row 1: [5 5.332542] Row 1: [5 5.332542]
可以看到,format()和printf()是等價的,他們只需要一個簡單的格式化字符串,加上一串參數即可,每個參數對應一個格式修飾符。
2、Formatter類
在Java中,所有的字符串格式化功能都由java.util.Formatter類處理的。可以將Formatter看做一個翻譯器,它將你的格式化字符串與數據翻譯成需要的結果。當你創建一個Formatter對象的時候,需要向其構造器傳遞一些信息,告訴它最終的結果將向哪里輸出:
import java.io.*; import java.util.*; public class Turtle { private String name; private Formatter f; public Turtle(String name,Formatter f) { this.name = name; this.f = f; } public void move(int x,int y) { f.format("%s The Turtle is at (%d,%d)\n",name,x,y); } public static void main(String[] args) { PrintStream outAlias = System.out; Turtle tommy = new Turtle("Tommy",new Formatter(System.out)); Turtle terry = new Turtle("Terry",new Formatter(outAlias)); tommy.move(0, 0); terry.move(4, 8); tommy.move(3, 4); terry.move(2, 5); tommy.move(3, 3); terry.move(3, 3); } }
輸出如下:
Tommy The Turtle is at (0,0) Terry The Turtle is at (4,8) Tommy The Turtle is at (3,4) Terry The Turtle is at (2,5) Tommy The Turtle is at (3,3) Terry The Turtle is at (3,3)
所有的tommy都將輸出到System.out,而所有的terry則輸出到System.out的一個別名上,Formatter構造器經過重載可以接受多種輸出目的地,不過最常用的還是PrintStream()、OutputStream和File。
3、格式化說明符
在插入數據時,如果想要控制空格與對齊,則需要使用更精細復雜的格式修飾符。以下是其抽象的語法:
%[argument_index$][flags][width][.precision]conversion
- argument_index$:argument_index 是一個十進制整數,用於表明參數在參數列表中的位置。第一個參數由 “1$” 引用,第二個參數由 “2$” 引用,依此類推;
- flags:可選 flags 是修改輸出格式的字符集。有效標志集取決於轉換類型;
- width:控制一個域的最小值,默認情況下下是右對齊的,不過可以通過使用“-”標志來改變對其方向;
- precision:精度,用於String時,表示輸出字符的最大數量,用於浮點數時,表示小數部分要顯示出來的位數(默認是6位),多則舍入,少則補0,用於整數會觸發異常;
- conversion:轉換格式,可選的格式有:
d | 整數型(十進制) |
c | Unicode字符 |
b | Boolean值 |
s | String |
f | 浮點數(十進制) |
e | 浮點數(科學計數) |
X | 整數(十六進制) |
h | 散列碼(十六進制) |
% | 字符串“%” |
注意當使用b作為轉換格式時,即Boolean,對於boolean基本類型或者Boolean對象,其轉換結果是對應的true或false。但是對於其他類型的參數,只要該參數不為null,那么該轉換的結果就永遠都是true。0也會轉換為true的,跟其他語言有所區別。所以將b運用於非布爾類型要注意。
下面的程序應用格式修飾符來打印一個購物收據:
import java.util.*; public class Receipt { private double total = 0; private Formatter f = new Formatter(System.out); public void printTitle() { f.format("%-15s %5s %10s\n","Item","Qty","Price"); f.format("%-15s %5s %10s\n","---","---","-----"); } public void print(String name,int qty,double price) { //%-15.15s:字符串左對齊,最小長度為15,最大長度也為15 //%10.2f:最小長度為10,其中小數占兩位 f.format("%-15.15s %5d %10.2f\n",name,qty,price); total += price; } public void printTotal() { f.format("%-15s %5s %10.2f\n","Tax","",total*0.06); f.format("%-15s %5s %10s\n","","","-----"); f.format("%-15s %5s %10.2f\n","Total","",total*1.06); } public static void main(String[] args) { Receipt receipt = new Receipt(); receipt.printTitle(); receipt.print("Jack's Magic Beans",4, 4.25); receipt.print("Princess Peas",3, 5.1); receipt.print("Three Bears Porridge",1,14.29); receipt.printTotal(); } }
輸出如下:
Item Qty Price --- --- ----- Jack's Magic Be 4 4.25 Princess Peas 3 5.10 Three Bears Por 1 14.29 Tax 1.42 ----- Total 25.06
4、String.format()
Java SE5參考了C語言中的sprintf()方法,以生成格式化的String對象。String.format()是一個靜態方法,它接受與Formatter.format()方法一樣的參數,但是返回一個String對象。
下面演示一個String.format()的例子:
public class DatabaseException extends Exception{ public DatabaseException(int transactionID,int queryID,String message) { //調用基類構造函數 super(String.format("(t%d,q%d) %s", transactionID,queryID,message)); } public static void main(String[] args) { try { throw new DatabaseException(3,7,"Write filed!"); }catch(Exception e) { System.out.println(e.getMessage()); } } }
輸出如下:
(t3,q7) Write filed!
其實在String.format()的內部,也是創建了一個Formatter對象,然后將傳入的參數轉給該Formatter。不過,與其自己做這些事情,不如使用快捷的String.format()方法,何況這樣的代碼更清晰易懂。
三 正則表達式
正則表達式是一種強大而靈活的文本處理工具。
- 正則表達式定義了字符串的模式;
- 正則表達式可以用來搜索、編輯或處理文本;
- 正則表達式並不僅限於某一種語言,但是在每種語言中有細微的差別;
1、正則表達式實例
一個字符串其實就是一個簡單的正則表達式,例如 Hello World 正則表達式匹配 "Hello World" 字符串。
.(點號)也是一個正則表達式,它匹配任何一個字符如:"a" 或 "1"。
下表列出了一些正則表達式的實例及描述:
正則表達式 | 描述 |
---|---|
this is text |
匹配字符串 "this is text" |
this\s+is\s+text |
注意字符串中的 \s+。 匹配單詞 "this" 后面的 \s+ 可以匹配多個空格,之后匹配 is 字符串,再之后 \s+ 匹配多個空格然后再跟上 text 字符串。 可以匹配這個實例:this is text |
^\d+(\.\d+)? |
^ 定義了以什么開始 \d+ 匹配一個或多個數字 ? 設置括號內的選項是可選的 \. 匹配 "." 可以匹配的實例:"5", "1.5" 和 "2.21"。 |
注意:
在其它語言中,\\ 表示:我想要在正則表達式中插入一個普通的(字面上的)反斜杠,請不要給它任何特殊的意義。
在 Java 中,\\ 表示:我要插入一個正則表達式的反斜線,所以其后的字符具有特殊的意義。
所以,在其它的語言中(如Perl),一個反斜杠 \ 就足以具有轉義的作用,而在 Java 中正則表達式中則需要有兩個反斜杠才能被解析為其它語言中的轉義作用。也可以簡單的理解在 Java 的正則表達式中,兩個 \\ 代表其他語言中的一個 \,因此,在Java中表示一位數字的正則表達式為\\d,而表示一個普通的反斜杠為 \\\\。
Java 正則表達式和 Perl 的是最為相似的。
java.util.regex 包主要包括以下三個類:
- Pattern 類:
pattern 對象是一個正則表達式的編譯表示。Pattern 類沒有公共構造方法。要創建一個 Pattern 對象,你必須首先調用其公共靜態編譯方法,它返回一個 Pattern 對象。該方法接受一個正則表達式作為它的第一個參數。比如:
Pattern r = Pattern.compile("(\\D*)(\\d+)(.*)");
- Matcher 類:
Matcher 對象是對輸入字符串進行解釋和匹配操作的引擎。與Pattern 類一樣,Matcher 也沒有公共構造方法。你需要調用 Pattern 對象的 matcher 方法來獲得一個 Matcher 對象。比如:Matcher m = r.matcher("This order was placed for QT3000! OK?");
- PatternSyntaxException:
PatternSyntaxException 是一個非強制異常類,它表示一個正則表達式模式中的語法錯誤。
以下實例中使用了正則表達式 .*runoob.* 用於查找字符串中是否包了 runoob 子串:
import java.util.regex.*; class RegexExample1{ public static void main(String args[]){ String content = "I am noob " + "from runoob.com."; String pattern = ".*runoob.*"; boolean isMatch = Pattern.matches(pattern, content); System.out.println("字符串中是否包含了 'runoob' 子字符串? " + isMatch); } }
輸出結果:
字符串中是否包含了 'runoob' 子字符串? true
2、捕獲組
捕獲組是把多個字符當一個單獨單元進行處理的方法,它通過對括號內的字符分組來創建。
例如,正則表達式 (dog) 創建了單一分組,組里包含"d","o",和"g"。
捕獲組是通過從左至右計算其開括號來編號。例如,在表達式((A)(B(C))),有四個這樣的組:
- ((A)(B(C)))
- (A)
- (B(C))
- (C)
可以通過調用 matcher 對象的 groupCount ()方法來查看表達式有多少個分組。groupCount() 方法返回一個 int 值,表示matcher對象當前有多個捕獲組。
還有一個特殊的組(group(0)),它總是代表前一次匹配操作(例如find())的結果。該組不包括在 groupCount 的返回值中。
下面的例子說明如何從一個給定的字符串中找到數字串:
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("Found value: " + m.group(0) ); System.out.println("Found value: " + m.group(1) ); System.out.println("Found value: " + m.group(2) ); System.out.println("Found value: " + m.group(3) ); } else { System.out.println("NO MATCH"); } } }
以上實例編譯運行結果如下:
Found value: This order was placed for QT3000! OK? //第一次匹配到的一個子字符串 Found value: This order was placed for QT //第一個分組 Found value: 3000 //第二個分組 Found value: ! OK? //第三個分組
3、正則表達式語法
字符 |
說明 |
---|---|
\ |
將下一字符標記為特殊字符、文本、反向引用或八進制轉義符。例如,"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?'。注意:您不能將空格插入逗號和數字之間。 |
? |
當此字符緊隨任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后時,匹配模式是"非貪心的"。"非貪心的"模式匹配搜索到的、盡可能短的字符串,而默認的"貪心的"模式匹配搜索到的、盡可能長的字符串。例如,在字符串"oooo"中,"o+?"只匹配單個"o",而"o+"匹配所有"o"。 |
. |
匹配除"\r\n"之外的任何單個字符。若要匹配包括"\r\n"在內的任意字符,請使用諸如"[\s\S]"之類的模式。 |
(pattern) |
匹配 pattern 並捕獲該匹配的子表達式。可以使用 $0…$9 屬性從結果"匹配"集合中檢索捕獲的匹配。若要匹配括號字符 ( ),請使用"\("或者"\)"。 |
(?:pattern) |
匹配 pattern 但不捕獲該匹配的子表達式,即它是一個非捕獲匹配,不存儲供以后使用的匹配。這對於用"or"字符 (|) 組合模式部件的情況很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更經濟的表達式。 |
(?=pattern) |
執行正向預測先行搜索的子表達式,該表達式匹配處於匹配 pattern 的字符串的起始點的字符串。它是一個非捕獲匹配,即不能捕獲供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。預測先行不占用字符,即發生匹配后,下一匹配的搜索緊隨上一匹配之后,而不是在組成預測先行的字符后。 |
(?!pattern) |
執行反向預測先行搜索的子表達式,該表達式匹配不處於匹配 pattern 的字符串的起始點的搜索字符串。它是一個非捕獲匹配,即不能捕獲供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"。預測先行不占用字符,即發生匹配后,下一匹配的搜索緊隨上一匹配之后,而不是在組成預測先行的字符后。 |
x|y |
匹配 x 或 y。例如,'z|food' 匹配"z"或"food"。'(z|f)ood' 匹配"zood"或"food"。 |
[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。 |
\r |
匹配一個回車符。等效於 \x0d 和 \cM。 |
\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"匹配兩個連續的相同字符。 |
\n |
標識一個八進制轉義碼或反向引用。如果 \n 前面至少有 n 個捕獲子表達式,那么 n 是反向引用。否則,如果 n 是八進制數 (0-7),那么 n是八進制轉義碼。 |
\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 匹配版權符號 (©)。 |
根據 Java Language Specification 的要求,Java 源代碼的字符串中的反斜線被解釋為 Unicode 轉義或其他字符轉義。因此必須在字符串字面值中使用兩個反斜線,表示正則表達式受到保護,不被 Java 字節碼編譯器解釋。例如,當解釋為正則表達式時,字符串字面值 "\b" 與單個退格字符匹配,而 "\\b" 與單詞邊界匹配;字符串字面值 "\(hello\)" 是非法的,將導致編譯時錯誤;要與字符串 (hello) 匹配,必須使用字符串字面值 "\\(hello\\)"。
4、Pattern.compile()
Pattern類的compile()方法除了只有一個正則表達式參數的版本,還有另一個版本:
Pattern Pattern.compile(String regex, int flag)
其中的flag來自以下Pattern類中的常量:
- Pattern.CANON_EQ,當且僅當兩個字符的"正規分解(canonical decomposition)"都完全相同的情況下,才認定匹配。比如用了這個標志之后,表達式"a\u030A"會匹配"?"。默認情況下,不考慮"規范相等性(canonical equivalence)";
- Pattern.CASE_INSENSITIVE(?i) 默認情況下,大小寫不敏感的匹配只適用於US-ASCII字符集。這個標志能讓表達式忽略大小寫進行匹配。要想對Unicode字符進行大小不明感的匹 配,只要將UNICODE_CASE與這個標志合起來就行了;
- Pattern.COMMENTS(?x) 在這種模式下,匹配時會忽略(正則表達式里的)空格字符(不是指表達式里的"\\s",而是指表達式里的空格,tab,回車之類)。注釋從#開始,一直到這行結束。可以通過嵌入式的標志來啟用Unix行模式;
- Pattern.DOTALL(?s) 在這種模式下,表達式'.'可以匹配任意字符,包括表示一行的結束符。默認情況下,表達式'.'不匹配行的結束符;
- Pattern.MULTILINE(?m)在這種模式下,'^'和'\$'分別匹配一行的開始和結束。此外,'^'仍然匹配字符串的開始,'\$'也匹配字符串的結束。默認情況下,這兩個表達式僅僅匹配字符串的開始和結束;
- Pattern.UNICODE_CASE(?u) 在這個模式下,如果你還啟用了CASE_INSENSITIVE標志,那么它會對Unicode字符進行大小寫不明感的匹配。默認情況下,大小寫不敏感的匹配只適用於US-ASCII字符集;
- Pattern.UNIX_LINES(?d) 在這個模式下,只有'\n'才被認作一行的中止,並且與'.','^',以及'$'進行匹配。
在這些標記中,Pattern.CASE_INSENSITIVE、Pattern.MULTILINE以及Pattern.COMMENTS特別有用。
下面演示一個多個標志位組合的案例:
import java.util.regex.*; public class RegexExample7 { public static void main(String[] args) { Pattern p = Pattern.compile("^java",Pattern.CASE_INSENSITIVE|Pattern.MULTILINE); Matcher m = p.matcher( "java has regex\njava has regex\n"+ "Java has pretty good regular expression\n" + "My name is Java" ); while(m.find()) { System.out.println(m.group()); } } }
輸出:
java
java
Java
在這個例子中,創建了一個模式,它將匹配所有以"java"、"Java"和“JAVA”等開頭的行,並且是在設置了多行標記的狀態下,對每一個行(從字符序列的第一個字符開始,至每一個行終結符)都進行匹配。注意:group()只返回已匹配的部分。
5、Matcher 類
(1) 索引方法
索引方法提供了有用的索引值,精確表明輸入字符串中在哪能找到匹配:
序號 | 方法及說明 |
---|---|
1 | public int start() 返回以前匹配的初始索引。 |
2 | public int start(int group) 返回在以前的匹配操作期間,由給定組所捕獲的子序列的初始索引 |
3 | public int end() 返回最后匹配字符之后的偏移量。 |
4 | public int end(int group) 返回在以前的匹配操作期間,由給定組所捕獲子序列的最后字符之后的偏移量。 |
下面是一個對單詞 "cat" 出現在輸入字符串中出現次數進行計數的例子:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { 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
可以看到這個例子是使用單詞邊界,以確保字母 "c" "a" "t" 並非僅是一個較長的詞的子串。它也提供了一些關於輸入字符串中匹配發生位置的有用信息。
Start 方法返回在以前的匹配操作期間,由給定組所捕獲的子序列的初始索引,end 方法最后一個匹配字符的索引加 1。
(2) 研究方法
研究方法用來檢查輸入字符串並返回一個布爾值,表示是否找到該模式:
序號 | 方法及說明 |
---|---|
1 | public boolean lookingAt() 嘗試將從區域開頭開始的輸入序列與該模式匹配。 |
2 | public boolean find() 嘗試查找與該模式匹配的輸入序列的下一個子序列。 |
3 | public boolean find(int start) 重置此匹配器,然后嘗試查找匹配該模式、從指定索引開始的輸入序列的下一個子序列。 |
4 | public boolean matches() 嘗試將整個區域與模式匹配。 |
matches 和 lookingAt 方法都用來嘗試匹配一個輸入序列模式。它們的不同是 matches 要求整個序列都匹配,而lookingAt 不要求。
lookingAt 方法雖然不需要整句都匹配,但是需要從第一個字符開始匹配。
這兩個方法經常在輸入字符串的開始使用。
我們通過下面這個例子,來解釋這個功能:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { 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
(3) 替換方法
替換方法是替換輸入字符串里文本的方法:
序號 | 方法及說明 |
---|---|
1 | public Matcher appendReplacement(StringBuffer sb, String replacement) 實現非終端添加和替換步驟。 |
2 | public StringBuffer appendTail(StringBuffer sb) 實現終端添加和替換步驟。 |
3 | public String replaceAll(String replacement) 替換模式與給定替換字符串相匹配的輸入序列的每個子序列。 |
4 | public String replaceFirst(String replacement) 替換模式與給定替換字符串匹配的輸入序列的第一個子序列。 |
5 | public static String quoteReplacement(String s) 返回指定字符串的字面替換字符串。這個方法返回一個字符串,就像傳遞給Matcher類的appendReplacement 方法一個字面字符串一樣工作。 |
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
replaceFirst 和 replaceAll 方法用來替換匹配正則表達式的文本。不同的是,replaceFirst 替換首次匹配,replaceAll 替換所有匹配。
下面的例子來解釋這個功能:
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { 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.
6、PatternSyntaxException 類的方法
PatternSyntaxException 是一個非強制異常類,它指示一個正則表達式模式中的語法錯誤。
PatternSyntaxException 類提供了下面的方法來幫助我們查看發生了什么錯誤。
序號 | 方法及說明 |
---|---|
1 | public String getDescription() 獲取錯誤的描述。 |
2 | public int getIndex() 獲取錯誤的索引。 |
3 | public String getPattern() 獲取錯誤的正則表達式模式。 |
4 | public String getMessage() 返回多行字符串,包含語法錯誤及其索引的描述、錯誤的正則表達式模式和模式中錯誤索引的可視化指示。 |
參考文獻
[1]Java中的String,StringBuilder,StringBuffer三者的區別
[2] Java 正則表達式(部分轉載)
[3]Java編程思想