字串處理

      在〈字串處理〉中尚無留言

目的

字串處理 (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毫秒

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *