程序开发中经常会碰到处理文本文件中数据的情况,这里通过一个例子来看用
java
实现文本文件按条件过滤的方法:从文本文件
employee.txt
中读取员工信息,从中找出
1981
年
1
月
1
日(含)之后出生的女员工。
文本文件
empolyee.txt
的格式如下:
EID NAME SURNAME GENDER STATE BIRTHDAY HIREDATE DEPT SALARY
1 Rebecca Moore F California 1974-11-20 2005-03-11 R&D 7000
2 Ashley Wilson F New York 1980-07-19 2008-03-16 Finance 11000
3 Rachel Johnson F New Mexico 1970-12-17 2010-12-01 Sales 9000
4 Emily Smith F Texas 1985-03-07 2006-08-15 HR 7000
5 Ashley Smith F Texas 1975-05-13 2004-07-30 R&D 16000
6 Matthew Johnson M California 1984-07-07 2005-07-07 Sales 11000
7 Alexis Smith F Illinois 1972-08-16 2002-08-16 Sales 9000
8 Megan Wilson F California 1979-04-19 1984-04-19 Marketing 11000
9 Victoria Davis F Texas 1983-12-07 2009-12-07 HR 3000
10 Ryan Johnson M Pennsylvania 1976-03-12 2006-03-12 R&D 13000
11 Jacob Moore M Texas 1974-12-16 2004-12-16 Sales 12000
12 Jessica Davis F New York 1980-09-11 2008-09-11 Sales 7000
13 Daniel Davis M Florida 1982-05-14 2010-05-14 Finance 10000
…
Java
程序的编写思路是从文件逐行读入数据保存到
List
对象中,再遍历
List
对象,如果满足条件就保存到结果
List
对象中,最后打印出符合条件的员工个数。具体代码如下:
public static void myFilter() throws Exception{
File file = newFile("D:\\employee.txt");
FileInputStream fis = null;
fis = new FileInputStream(file);
InputStreamReader input = newInputStreamReader(fis);
BufferedReader br = newBufferedReader(input);
String line = null;
String info[] = null;
List sourceList= new ArrayList();
List resultList= new ArrayList();
if ((line = br.readLine())== null)return;//
第一行舍弃,如果文件为空则退出
while((line = br.readLine())!= null){//
从文件中读入到内存中
info =line.split("\t");
Map<String,String>emp=new HashMap<String,String>();
emp.put("EID",info[0]);
emp.put("NAME",info[1]);
emp.put("SURNAME",info[2]);
emp.put("GENDER",info[3]);
emp.put("STATE",info[4]);
emp.put("BIRTHDAY",info[5]);
sourceList.add(emp);
}
for (int i = 0, len =sourceList.size(); i < len; i++) {//
逐行处理数据
Map<String,String> emp=(Map) sourceList.get(i);
SimpleDateFormat sdf = newSimpleDateFormat("yyyy-MM-dd");
if (emp.get("GENDER").equals("F") &&!sdf.parse(emp.get("BIRTHDAY")).before(sdf.parse("1981-01-01")))
{ //
条件判断语句,符合条件的加入
List
对象
resultList.add(emp);
}
}
System.out.println("count="+resultList.size());//
打印员工个数
}
这个函数的过滤条件是固定的,如果条件发生了变化,需要修改程序中的条件判断语句,
多种条件就写多段代码,无法处理临时性的动态条件
。下面改造一下这个代码,让它具备一定的通用性。只有遍历
sourceList
的循环有所改变:
for (int i = 0, len = sourceList.size(); i <len; i++) {
Map<String,String> emp=(Map) sourceList.get(i);
SimpleDateFormat sdf = newSimpleDateFormat("yyyy-MM-dd");
boolean isRight = true;
if (gender!=null &&!emp.get("GENDER").equals(gender)){//
处理性别条件
isRight = false;
}
if (start!=null && sdf.parse(emp.get("BIRTHDAY")).before(start)){//
处理生日的起始条件
isRight = false;
}
if (end!=null &&sdf.parse(emp.get("BIRTHDAY")).after(end) ){//
处理生日的结束条件
isRight = false;;
}
if (isRight)resultList.add(emp);//
符合条件的记录加入结果
list
}
改写之后的代码中,
gender、start、end
是函数
myFilter
的输入参数。程序可以处理
GENDER
字段等于输入值
gender,BIRTHDAY
字段大于等于输入值
start
,小于等于输入值
end
的情况。如果任何一个输入值为
null
,这个条件就不参与过滤。条件之间是
AND
关系。
如果希望
myFilter
更通用,比如:条件之间
有
OR
关系,或者字段之间发生了运算,那么代码就会更复杂了,需要编写动态表达式解析和求值的程序。这样的程序虽然可以像数据库的
SQL
那样灵活和通用,但编写难度实在是太大了。
这种情况下,可以采用用集算器
esProc
来辅助完成这个任务。集算器是专门为结构化(半结构化)数据处理设计的开发语言,实现上述通用查询任务很轻松,并和
Java
程序能无缝结合,从而使
Java
程序可以象
SQL
那样灵活访问和处理文本文件中的数据。
例如,我们需要查询
1981
年
1
月
1
日(含)之后出生的女员工,
esProc
程序可以从外部传入一个输入参数“
where
”作为动态的条件,如下图:
“
where
”的值是:
BIRTHDAY>=date(1981,1,1)&& GENDER=="F"
。具体的
esProc
代码只需要三行,如下:
A1
:定义一个
file
对象,导入数据,第一行是标题,字段分隔符默认是
tab
。
esProc
的集成开发环境可以直观的显示出导入的数据,如上图右边部分。
A2
:按照条件过滤。这里使用宏来实现动态解析表达式,其中的
where
就是传入参数。
集算器将先计算
${…}
里的表达式,将计算结果作为宏字符串值替换
${…}
之后解释执行。
这个例子中最终执行的是:
=A1.select(BIRTHDAY>=date(1981,1,1)&& GENDER=="F")
。
A3
:向外部程序返回符合条件的结果集。
过滤条件发生变化时不用改变程序,只需改变
where
参数即可。例如,条件变为:
查询
1981
年
1
月
1
日(含)之后出生的女员工,或者
NAME+SURNAME
等于
”RebeccaMoore”
的员工。
Where
的参数值可以写为:
BIRTHDAY>=date(1981,1,1)&& GENDER=="F" || NAME+SURNAME=="RebeccaMoore"
。执行之后,
A2
中的结果集如下图:
最后,还需要在
Java
程序中调用这段
esProc
程序获得过滤结果,使用
esProc
提供的
jdbc
即可完成。将上述
esProc
程序保存为
test.dfx
文件的话,
Java
调用的代码如下:
//
建立
esProc jdbc
连接
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
//
调用
esProc
程序(存储过程),其中
test
是
dfx
的文件名
st =(com.esproc.jdbc.InternalCStatement)con.prepareCall("calltest(?)");
//
设置参数
st.setObject(1,"BIRTHDAY>=date(1981,1,1) && GENDER==\"F\" ||NAME+SURNAME==\"RebeccaMoore\"");//
参数就是动态的过滤条件
//
执行
esProc
存储过程
st.execute();
//
获取结果集:符合条件的员工集合
ResultSet set = st.getResultSet();
对于代码较简单的脚本,还可以直接把集算器代码写在调用集算器
JDBC
的
Java
程序中,而不必专门编写集算器脚本文件(
test.dfx
):
st=(com. esproc.jdbc.InternalCStatement)con.createStatement();
ResultSetset=st.executeQuery("=file(\"D:\\\\esProc\\\\employee.txt\")
.import@t().select(BIRTHDAY>=date(1981,1,1)
&&GENDER==\"F\"|| NAME+SURNAME==\"RebeccaMoore\")");
这段
Java
代码直接调用了集算器的一句脚本:从文本文件中取得数据,并按照指定的条件过滤。结果集返回给
ResultSet
对象
set
。