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
hostName=mahaljsp.ddns.net
userName=user
password=pass
載入及使用Porperties File
Properties properties=new Properties(); try { FileInputStream fis=new FileInputStream("test.properties"); properties.load(fis); } catch (FileNotFoundException ex) {} catch (IOException ex) {} System.out.println("HostName : "+ properties.getProperty("HostName")); System.out.println("UserName : "+ properties.getProperty("UserName")); System.out.println("Password : "+ properties.getProperty("Password"));
test.properties要放在根目錄之下, 與manifest.mf同位置
Command Line載入Properties
System.out.println(“Test : “+System.getProperty(“usnername”));
然後於命令列下達 : java -Dusername=Thomas myApp
PrintWriter
PrinterWriter pw=new PrintWriter(System.out, true);
pw.println(“This is some output”);
PrinterWriter實作所有print的方法
printf格式
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);
可以使用格式化的, 有
System.out.printf
System.out.format
String.format
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″); x的結果還是 “Java”, 因為並沒有把新的字串指給x
StringBuffer
StringBuffer同String, 但字串相加使用 append, 且字串內容是可以改變的. StringBuffer 是thread-safe, 但效能比String好很多
StringBuffer x=new StringBuffer(“Java”);
x.append(” OCP”).append(” 804″);
x的值會變更為 “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);
String 常用方法
equals(), contains(), replace()
substring(啟始字, 結束字)
substring啟始字由0開始, 結束字由1開始
StringBuilder s=new StringBuilder(“123456789”);
s.substring(0,2);
s還是”0123456789″, 因為substring是會傳回取出的新字串, 而上面並沒有接收新字串
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()); }
同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); } }
useDelimiter(“, “)<==指是是要分割的字串Delimiter[dɪˋlɪmɪtɚ]定義符號
原生資料物件化
–todo start
原生資料不是物件, 所以不能參與物件導向的機制. 所以把原生資料包裝(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()
–todo end
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毫秒