编程中有时需要将一段文本分解成标记,比如说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