目的
字串處理 (String Processing) 目的如下幾項
從command line讀取資料
搜尋字串
Parse strings
StringBuilder
正規法搜尋
正規法Parse string
正規法取代字串
命令列參數
java TestArgs arg1 arg2 “another arg”,每個命令列參數都放置在陣列中,然後 pass 給 main() 方法裏的String[] args 參數。
Properties
以 “.properties” 為副檔名的檔案,可以存放 key-value 的值供 java 使用,如 test.properties。請注意 test.properties 要放在專案根目錄之下,與 manifest.mf 同位置。
hostName=mahaljsp.ddns.net userName=user password=pass
載入及使用 Porperties File
package stringtest; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; public class StringTest { public static void main(String[] args) { Properties p=new Properties(); try { FileInputStream fis=new FileInputStream("test.properties"); p.load(fis); System.out.println(p.getProperty("hostName")); System.out.println(p.getProperty("userName")); System.out.println(p.getProperty("password")); } catch (FileNotFoundException ex) { Logger.getLogger(StringTest.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(StringTest.class.getName()).log(Level.SEVERE, null, ex); } } }
Command Line 載入 Properties
System 本身也有 Properties,可以取得命令列傳入的 key-value。
package stringtest; import java.util.Properties; public class StringTest { public static void main(String[] args) { Properties p=System.getProperties(); System.out.println(p.getProperty("userName")); System.out.println(p.getProperty("password")); } }
然後於命令列執行如下指令,請注意 -D 後面不能有空格。
java -DuserName=Thomas -Dpassword=123456 .\stringtest\StringTest.java
PrintWriter
PrinterWriter pw=new PrintWriter(System.out, true);
pw.println(“This is some output”);
PrinterWriter實作所有print的方法
字串格式化
常用的字串格式化如下
System.out.printf System.out.format String.format
底下為各種方法的範例
System.out.printf("顏色 : %s, 數量 : %d, 單價 : $%3.2f\n", "Blue", 10, 10.5); System.out.format("顏色 : %s, 數量 : %d, 單價 : $%3.2f\n", "Blue", 10, 10.5); String out=String.format("顏色 : %s, 數量 : %d, 單價 : $%3.2f\n", "Blue", 10, 10.5); System.out.println(out); PrintWriter pw=new PrintWriter(System.out, true); pw.printf("顏色 : %s, 數量 : %d, 單價 : $%3.2f\n", "Blue", 10, 10.5);
String
String 的內容是 immutable (永久不變的),String 可使用 + 及 concat() 結合二字串。
String x="Java";
x=x.concat(" OCP");// 新的字串 "Java OCP" 會重新指定給 x
concat() 的效能比 “+” 好很多,且 concat() 可以一直連續使用。
x=x.concat(" OCP").concat(" 804");
請注意如下
String x="Java";
x.concat(" OCP").concat(" 804");
結果 :
Java
x 的結果還是 "Java", 因為並沒有把新的字串指給x
StringBuffer
StringBuffer 同 String,但字串相加使用 append,且字串內容是可以改變的。StringBuffer 是thread-safe,效能比 String 好很多。
StringBuffer x=new StringBuffer("Java");
x.append(" OCP").append(" 804");
結果 :
Java OCP 804
StringBuilder
同 StringBuffer,不過是 non-Thread-Safe,所以效能又比 StringBuffer 更好。
StringBuilder sb=new StringBuilder(); sb.append(", 中山路一段\n"); sb.insert(0, "彰化市"); sb.append("["); for(int i=0;i<=10;i++){ sb.append(i).append(" "); } sb.append("]"); System.out.println(sb); 結果 : 彰化市, 中山路一段 [0 1 2 3 4 5 6 7 8 9 10 ]
String 常用方法
equals()
contains()
replace()
substring(啟始, 結束)
substring 啟始由 0 開始,結束不包含
StringBuilder sb=new StringBuilder("abcdefgh"); String s=sb.substring(0,4); 結果 : abcd
StringBuilder 經過 substring 後,傳回的型態為 String。
split()方法
String str="This is a computer";
String[] array=str.split(" ");
for(String s:array){
System.out.println(s);
}
StringTokenizer(tokenizer標記解析器)
String str="Blue Shirt, Red Shirt, Black Shirt, Maroon Shirt"; StringTokenizer st=new StringTokenizer(str, ", "); while(st.hasMoreTokens()){ System.out.println(st.nextToken()); } 結果 : Blue Shirt Red Shirt Black Shirt Maroon Shirt
同 split() 方法,但分割可以是多個字,如 “, abc” 表示遇到 “,” 或 a or b or c,都進行分割,並使用 iterate 存取分割後的結果。
Scanner類別
可以解析字串,並用 nextXXX() 取出原生資料。
String str="1.11, 2.22, 3.33, 4.44, 5.55"; Scanner s=new Scanner(str).useDelimiter((", "));//指定要分割的字串 while(s.hasNextFloat()){ float f=s.nextFloat(); System.out.println(f); } 結果 : 1.11 2.22 3.33 4.44 5.55
Delimiter[dɪˋlɪmɪtɚ]定義符號。
原生資料物件化
原生資料不是物件,所以不能參與物件導向的機制。所以把原生資料包裝 (wrapper) 成物件,就是最佳的解決方式。包裝後的物件對應的類別如下
char : Character byte : Byte short : Short int : Integer long : Long float : Float double : Double boolean : Boolean
字串數字互轉
字串轉數字、數字轉字串的範例如下。
int x=Integer.parseInt("100"); String s=String.valueOf(100);
自動裝箱拆箱
int i=10; Integer o=new Integer(i); o=i;//Auto box i=0;//Auto unbox
數字池
-128~127的數字會在數字池產生物件, 同字串池。
方法重載
尋找接收方法的機制
1. 先尋找能向上轉型的方法-int->long->float->double
2. 啟動自動裝箱拆箱機制
3. 找尋varags的方法
Switch()支援
switch支援byte, char, short, int, String. 而且也支援Enumerated, 只不過是編譯器在switch裏自動加上i.intValue()
java.text package
DecimalFormat
可以利用DecimalFormat創造一個物件, 再利用此物件的format()方法接收數字, 傳出想要的數字格式
DecimalFormat的建構子可人接收pattern,
#預告一個數字, 若該位置沒有數值則不顯示
0預告一個數字, 若該位置沒有數值則以 0 顯示
public class TextTest { public static void main(String[] args) { DecimalFormat d=new DecimalFormat("000,000,000.00"); System.out.println("Total : "+ new DecimalFormat("000,000,000.00").format(5000)); } }
DateFormat
DateFormat為抽像類別, 利用三種方法取得物件實体
DateFormat.getDateInstance() : 日期格式
DateFormat.getTimeInstance() : 時間格式
DateFormat.getDateTimeInstance() : 日期時間格式
然後再利用實体的format()方法接收日期時間
public class TextTest { public static void main(String[] args) { DateFormat df1=DateFormat.getDateInstance(DateFormat.SHORT, Locale.TAIWAN); DateFormat df2=DateFormat.getTimeInstance(DateFormat.FULL, Locale.TAIWAN); DateFormat df3=DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.TAIWAN); System.out.println(df1.format(new Date())); System.out.println(df2.format(new Date())); System.out.println(df3.format(new Date())); } }
DateFormat的日期時間格式有
DateFormat.SHORT
DateFormat.MEDIUM
DateFormat.LONG
DateFormat.FULL
區域物件使用Locate.xxx : 見第15章
df.parse(字串) : 可將字串轉成Date物件
Date
Date的建構子有二種
Date() : 取得目前時間
Date(long) : 自1970/01/01 : 0:0:0 到 long的時間, 單位為毫秒
Date d=new Date();
d.getTime() : 取得目前時間(毫秒數)
d.setTime(1000*2*86400+d.getTime());設定為二天後的時間
正規表示法(Regular Expressions)
正規表示法, 是用來搜尋字串的樣式, 很不好記, 但又很愛考, 所以很頭大
Pattern正規樣式
. | 任意一個字元 |
* | 任意多個字元, 包含0 |
+ | 任意多個字元, 不包含0 |
? | 任意0個或1個字元 |
– | 範圍 |
| | 分隔樣式 |
^ | 一行的開頭 |
$ | 一行的結尾 |
\b | 開始或結尾的字串 |
\B | 不是開頭或結尾 的字串 |
[] | 在[]中的任意一個字元 |
[^] | 不在[]中的任意一個字元 |
{n} | 包含前面n個字元 |
{n,} | 包含前面n個字元以上 |
{n,m} | 包含前面n個字元以上, m個以下 |
Pattern | 結果 |
ABC. | ABCD ABC2 …. |
A.B | ACB AZB |
A*B | ACB AXYB |
ABC[XYZ] | ABCX ABCY ABCZ |
ABC[A-F] | ABCA ABCB ABCC ABCD ABCE ABCF |
ABC[A|ABC|1234] | ABCA ABCABC ABC1234 |
ABC[A-F]{2,3} | ABCEF ABCACD |
Pattern類別
先使用Pattern.compile(String) 產生一個要過濾的物件p, 再使用物件p.matches(String), p.find(String), p.lookingAt(String)產生Matcher的物件
public class PatternTest { public static void main(String[] args) { Pattern p=Pattern.compile("".*WAS.*", Pattern.CASE_INSENSITIVE"); Matcher m=p.matcher("I was been there before"); System.out.println(m.matches()); } }
Pattern.compile()第一個參數即為模版, 上述的 “.*” 是指任意多個字元, 不可以只寫 “*”. 第二個參數為比對旗標, 有
CANNOT_EQ: 完全正規化相等模式
CASE_INSENSITIVE : 忽略大小寫
p.matcher(String)即把要過濾的原字串傳入, 然後產生Matcher的物件 m. m有三個方法傳回比對的結果
m.matches() : 必需整個字串完全符合過濾模板 : 傳回true/false
m.find() : 從上一次比較結束的地方開始, 與模板比對, 有找到傳回true, 否則傳回false. 找到後, 可從m.start()及m.end()取得開始及結束的位置
m.lookingAt() :
m.replaceAll(“is”), 會先使用find的方式找到模版的字, 再將之取代掉
String t="It was the best of times"; Pattern p1=Pattern.compile("w.s"); Matcher m1=p1.matcher((t)); if(m1.find()){ System.out.println(m1.group()); }
m1.group() 結果為 “was”
String t="Longlonglong ago, in a galaxy far far away"; Pattern p1=Pattern.compile("ago.*"); Pattern p2=Pattern.compile("gal.{6}"); Pattern p3=Pattern.compile("(long){2}"); Matcher m; m=p1.matcher((t)); if(m.find())System.out.println(m.group()); m=p2.matcher((t)); if(m.find())System.out.println(m.group()); m=p3.matcher((t)); if(m.find())System.out.println(m.group()); 結果為 ago, in a galaxy far far away galaxy fa longlong
貪婪的截取
Pattern p4=Pattern.compile("ago.*far"); m=p4.matcher((t)); if(m.find())System.out.println(m.group()); Pattern p5=Pattern.compile("ago.*?far"); m=p5.matcher((t)); if(m.find())System.out.println(m.group()); 結果 : ago, in a galaxy far far ago, in a galaxy far
正規取法, 會一直截取下去, 直到最後, 或者最後一個字串為止
String t="it was the best of times or it was the worst of times"; Pattern p1=Pattern.compile("^it.*?times"); Pattern p2=Pattern.compile("\\sit.*times$"); Pattern p3=Pattern.compile("\\bor\\b.{3}"); Matcher m; m=p1.matcher((t)); if(m.find())System.out.println(m.group()); m=p2.matcher((t)); if(m.find())System.out.println(m.group()); m=p3.matcher((t)); if(m.find())System.out.println(m.group()); 結果 : it was the best of times it was the worst of times or it
Pattern物件的split()方法
先使用Pattern.compile()傳入切割字元, 再用p.split()傳入要切割的字串, 第二個參數是指要切割幾次的意思
public class PatternTest { public static void main(String[] args) { Pattern p=Pattern.compile("[:/.]+"); String[] s=p.split("http://mahaljsp.ddns.net",3); for (String str:s){ System.out.println(str); } } }
String也有split(), 其實是調用Pattern的split()
預定義字元類別(Predefined Character class)
\d : 表示[0-9]
\w : 表示[a-2A-Z0-9]
\s : 表示 空白, 包含 \r \t \n \f \0XB
如下例
\\d\\d包含2個數字
\\sin\\s : in的前後都是空白
\\Sin\\S : in的前後都不是空白
String t="It was the best of times 50 20"; Pattern p1=Pattern.compile("\\d\\d"); Matcher m1=p1.matcher((t)); while(m1.find()){ System.out.println(m1.group()); }
以上會顯示 50 20
Formatter格式化輸出工具
public static void main(String[] args) { Formatter f=new Formatter(System.out); f.format("PI = %f", Math.PI); }
先建立Formatter物件, 建構子可以是輸出地點, 如StringBuffer(可以append的), 檔案, OutputStream
再用format()方法將之輸出
System.out.printf()及使用了Formatter的方式來列印
System.out.format()同printf();
Scanner
簡易的I/O工具
Scanner sc=new Scanner(System.in); //自鍵盤輸入一字串
int i=sc.nextInt(); //輸入數字
String str=sc.next(); //輸入字串
Scanner sc=new Scanner(“Java,Hello”);//自字串取得資料
sc.useDelimiter(“,”);//分隔符號
sc.next(); //取出 “Java”
Scanner sc=new Scanner(new File(“d:/test.txt”); //自檔案取得資料
sc.useDelimiter(“,|#”);//設定分隔符號
各種字串類別效能分析
使用String的 “+”, concat, 及StringBuffer, StringBuilder各相加10萬次, 效能比較如下
public static void main(String[] args) { String s=""; long t1, t2; t1=System.currentTimeMillis(); for (int i=0;i<100000;i++){ s=s+"a"; } t2=System.currentTimeMillis(); System.out.printf("String '+' 運作10萬次共廢時 : %d毫秒\n", t2-t1); s=""; t1=System.currentTimeMillis(); for (int i=0;i<100000;i++){ s=s.concat("a"); } t2=System.currentTimeMillis(); System.out.printf("String concat 運作10萬次共廢時 : %d毫秒\n", t2-t1); StringBuffer sb=new StringBuffer(); t1=System.currentTimeMillis(); for(int i=0;i<100000;i++){ sb.append("a"); } t2=System.currentTimeMillis(); System.out.printf("StringBuffer 運作10萬次共廢時 : %d毫秒\n", t2-t1); StringBuilder sbd=new StringBuilder(); t1=System.currentTimeMillis(); for(int i=0;i<100000;i++){ sbd.append("a"); } t2=System.currentTimeMillis(); System.out.printf("StringBuilder 運作10萬次共廢時 : %d毫秒\n", t2-t1); }
結果如下
String '+' 運作10萬次共廢時 : 4288毫秒 String concat 運作10萬次共廢時 : 1100毫秒 StringBuffer 運作10萬次共廢時 : 3毫秒 StringBuilder 運作10萬次共廢時 : 2毫秒