編程中有時需要將一段文本分解成標記,比如說14+2*3需要變成14,+,2,*,3的樣式,再比如說select a from b,需要變成select,a,from,b的形式,要寫出這樣的代碼不難,考慮到通用性,于是我制作了下面這個通用類,用戶只需要指定合法字符和分隔字符的正則表達式,程序即能將字符串分解成標記并注明類型,下面是源碼:
1.用于表示標記的Token類,含有文本和類型兩個屬性:
package com.heyang.tokenmaker;
/**
* 標記類,內含文本及類型
* 說明:
* 作者:heyang(heyang78@gmail.com)
*/
public class Token{
// 有效內容類型
public static final String Type_Content="Content";
// 分隔符類型
public static final String Type_Separator="Seperator";
// 標記文本
private String text;
// 標記類型
private String type;
/**
* 構造函數
* @param text
* @param type
*/
public Token(String text,String type){
this.text=text;
if(type.equals(Type_Content) || type.equals(Type_Separator)){
this.type=type;
}
else{
throw new IllegalArgumentException(type+"不是有效的類型。");
}
}
public String getText() {
return text;
}
public String getType() {
return type;
}
}
2.用于分解的TokenMaker類:
package com.heyang.tokenmaker;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
/**
* 傳入一個字符串,將它轉化為記號放在鏈表中
* 說明:
* 作者:何楊(heyang78@gmail.com)
*/
public class TokenMaker{
// 來源字符串
private String sourceString;
// 用正則表達式表示的,表示單個有效字符的字符串,注意這個是表示合法單字字符的正則表達式
private String validPatten;
// 用正則表達式表示的,表示單個分隔符的字符串,注意這個是表示合法單字字符的正則表達式
private String separatorPattern;
// 記號鏈表
private List<Token> tokens;
/**
* 構造函數
* @param sourceString
* @param validPatten
* @param seperatorPattern
* @throws Exception
*/
public TokenMaker(String sourceString,String validPatten,String seperatorPattern) throws Exception{
this.sourceString=sourceString;
this.validPatten=validPatten;
this.separatorPattern=seperatorPattern;
findTokens(sourceString);
}
/**
* 找到指定的標記并放入鏈表中
*
* 說明:
* @param sourceString
* @throws Exception
*/
private void findTokens(String sourceString)throws Exception{
tokens=new ArrayList<Token>();
sourceString=sourceString.toString();
final String End = "~";// 結束標志,這個地方注意與有效文本差別化
sourceString+=End;// 加上結束標志
// 單詞,用來累加字符
String word = "";
for (int i = 0; i < sourceString.length(); i++) {
// 取得每個字符
String str = String.valueOf(sourceString.charAt(i));
if(End.equals(str)){
// 將word放入鏈表
addTokenToList(new Token(word,Token.Type_Content));
break;
}
// 字符的驗證
if(isValid(str)==false){
throw new Exception("在"+this.sourceString+"找到非法的字符'"+str+"',無法進行求值.");
}
// 判斷是否空格
if (StringUtils.isBlank(str)) {
if (word.trim().length() < 1) {
// 碰到空格而word中沒有字符則從頭再來
continue;
} else {
// 將word放入鏈表
addTokenToList(new Token(word,Token.Type_Content));
// 然后吧word置空后繼續累加
word = "";
}
}
else if(isSeparator(str)){
// 將word放入鏈表
addTokenToList(new Token(word,Token.Type_Content));
// 將符號放入鏈表
addTokenToList(new Token(str,Token.Type_Separator));
// 然后吧word置空后繼續累加
word = "";
}
else {
// 不是則繼續累加
StringBuilder sb=new StringBuilder(word);
sb.append(str);
word=sb.toString();
//word += str;
}
}
}
/**
* 將標記添加到標記鏈表
*
* @param token
*/
private void addTokenToList(Token token){
if(token.getText().trim().length()>0){
tokens.add(token);
}
}
/**
* 打印鏈表中的標記
*
*/
public void printTokens(){
System.out.println("\n將文字"+sourceString+"轉化后的標記為:");
System.out.println("序號\t內容\t類型");
System.out.println("-------------------------");
int index=1;
for(Token token:tokens){
System.out.println((index++)+"\t"+token.getText()+"\t"+token.getType());
}
}
/**
* 判斷是否有效字符
*
* 說明:
* @param str
* @return
*/
private boolean isValid(String str){
Pattern p = Pattern.compile(validPatten,Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
return m.find();
}
/**
* 判斷是否分隔符
*
* 說明:
* @param str
* @return
*/
private boolean isSeparator(String str) {
Pattern p = Pattern.compile(separatorPattern,Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(str);
return m.find();
}
/**
* 取得標記鏈表
*
* 說明:
* @return
* 創建時間:2010-6-27 上午12:46:47
* 修改時間:2010-6-27 上午12:46:47
*/
public List<Token> getTokens() {
return tokens;
}
/**
* 取得標記的內容鏈表
*
* 說明:
* @return
* 創建時間:2010-6-27 上午08:59:34
* 修改時間:2010-6-27 上午08:59:34
*/
public List<String> getTokenConcents(){
List<String> ls=new ArrayList<String>();
for(Token token:tokens){
ls.add(token.getText());
}
return ls;
}
/**
* 測試
*
* 說明:
* @param args
* @throws Exception
* 創建時間:2010-6-27 上午09:00:02
* 修改時間:2010-6-27 上午09:00:02
*/
public static void main(String[] args) throws Exception{
new TokenMaker("96.2+8*5-12*(4-1)/2^(3%10)","[0-9\\.+-[*]/()\\^\\%]","[+-[*]/()\\^\\%]").printTokens();
new TokenMaker("select A a,b, v from ta,tb where 1=1 and 2=2 order by a asc","[\\w=<>!\\s,]","[\\s,]").printTokens();
}
}
3.對算式和Sql語句分解的結果:
將文字96.2+8*5-12*(4-1)/2^(3%10)轉化后的標記為:
序號 內容 類型
-------------------------
1 96.2 Content
2 + Seperator
3 8 Content
4 * Seperator
5 5 Content
6 - Seperator
7 12 Content
8 * Seperator
9 ( Seperator
10 4 Content
11 - Seperator
12 1 Content
13 ) Seperator
14 / Seperator
15 2 Content
16 ^ Seperator
17 ( Seperator
18 3 Content
19 % Seperator
20 10 Content
21 ) Seperator
將文字select A a,b, v from ta,tb where 1=1 and 2=2 order by a asc轉化后的標記為:
序號 內容 類型
-------------------------
1 select Content
2 A Content
3 a Content
4 , Seperator
5 b Content
6 , Seperator
7 v Content
8 from Content
9 ta Content
10 , Seperator
11 tb Content
12 where Content
13 1=1 Content
14 and Content
15 2=2 Content
16 order Content
17 by Content
18 a Content
19 asc Content