这周五临近下班的点,收到份任务——处理日期格式异常数据。那一刻我开始缅怀我那即将加班的周末。好吧,说一下这个任务的具体内容。XX公司dump一份数据过来,我们将数据导入自己的库然后开始清理日期格式异常数据然后再把他导出来还给XX公司。听起来挺简单的。当然后面会遇到意想不到的问题。且听下文分解。
周六中午,和往常加班一样我慢悠悠地出现在偌大空旷的办公室。从邮件下载dump文件,数据导入,一切进行的风平浪静。导数据的时候甚至还在想下午四点前能结束战斗出去找人冬至打边炉呢。然后噩梦就开始。
数据导入完成后,剩下的就是如何替换格式的问题了。第一步是将数据查询出来。低头一看,好家伙!日期格式异常的字段保存在一个凭证化的XML报文里。凭证信息又以zip形式存放在表中BLOB类型的data字段。这绝对是一次大字段存取的考验。菜都准备好,准备开工吧。
作为一个Javaer,我熟练地打开Eclipse开始寻找有没有已经写好的工具类。很不幸我们没有没有找到能用的工具类。自己写的BLOB字段读写方法读出来的是乱码。分析原因是XX公司存数据的时候使用了Zip压缩字节流。然后开始找度娘、谷哥帮忙。几番搜索,感谢度娘为软件事业做出的贡献。代码如下:
从BLOB字段中读出Zip压缩过的内容:
public staticString ClobToString(BLOB blob) {
StringreString = "";
ZipInputStreamzin = null;
ZipEntryentry = null;
InputStreammsgContent = null;
BufferedReaderbr = null;
StringBuffersbf = new StringBuffer();
try {
msgContent= blob.getBinaryStream();
zin= new ZipInputStream(msgContent);
while(zin!=null && (entry=zin.getNextEntry())!=null){
if(entry.isDirectory()||entry.getName().equals("..\\")){
continue;
}
String testString =entry.getComment();
br = newBufferedReader(newInputStreamReader(zin));
String line;
while((line = br.readLine()) !=null) {
sbf.append(line);
}
}
if(zin!=null) {
try {
zin.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(msgContent!=null) {
try {
msgContent.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(br!=null) {
try {
br.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}catch (Exception e) {
e.printStackTrace();
}
reString= new String(sbf);
returnreString;
}
将btyes数组Zip压缩:
public static byte[]zipBytes(byte[] bytes)throwsIOException {
ByteArrayOutputStream tempOStream = null;
BufferedOutputStream tempBOStream = null;
ZipOutputStream tempZStream = null;
ZipEntry tempEntry = null;
byte[] tempBytes =null;
tempOStream = newByteArrayOutputStream(bytes.length);
tempBOStream = newBufferedOutputStream(tempOStream);
tempZStream = newZipOutputStream(tempBOStream);
tempEntry = new ZipEntry(String.valueOf(bytes.length));
tempEntry.setMethod(ZipEntry.DEFLATED);
tempEntry.setSize((long) bytes.length);
tempZStream.putNextEntry(tempEntry);
tempZStream.write(bytes, 0, bytes.length);
tempZStream.flush();
tempBOStream.flush();
tempOStream.flush();
tempZStream.close();
tempBytes = tempOStream.toByteArray();
tempOStream.close();
tempBOStream.close();
return tempBytes;
}
接下来就是Mybatis的事情了。
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEmapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mappernamespace="org.mybatis.example.ChangXmlMapper">
<selectid="selectALLBloB"resultType="map"parameterType="map">
SELECTDATAID,DATA FROM XML_DATA
<!--SELECT DATAID,DATA FROM XML_DATA WHERE FINISHED IS NULL AND DATAID NOTIN('20113120610007033985') -->
</select>
<selectid="selectBloB"resultType="map"parameterType="map">
SELECTDATA FROM XML_DATA WHERE DATAID = #{DATAID}
</select>
<updateid="updateBloB" parameterType="map">
UPDATEXML_DATA SET DATA = #{DATA,jdbcType=BLOB},FINISHED = #{FINISHED} WHERE DATAID =#{DATAID}
</update>
</mapper>
头疼的事情来了。怎么对错误格式进行有效的查找和替换呢?XX公司还要求对不同的品目要有特殊的处理。刚开始都觉得使用正则表达式就可以搞定了。用正则表达式查找出各种异常的数据格式再根据替换为YYYY-MM-DD的正确格式。但是用HashSet找了一下部分数据的异常格式类型。点号分隔的、乱序的、全角字符的、中文汉字的、斜杠分隔的,什么鬼都有。心情顿时哇凉哇凉的。
2013.10-31;11月1日;2012年9月1日;11/30/2010;11/30/2011;11/30/2012;11/30/2013;2013-09-1;09.30;1013-11-01;2013-9-30;11/2/2013;2013年09月30日;2013-09-30;2013-10-31;1013-09-01;0;Friday, November01, 2013;2013-10.31;1/1/2013;2013年9月30日;2013-09-01;2013-011-01;11/30/3013;2013-09-01;11-01;11月30日;10.1;10-01;11/1/2103;2013年10月30日;2013 -10-01;2013-10-02;2013-10-01;2013-12-1;10.01;2013-12-31;2013-10-04;2013-11-30;2013-10-03;2013-10.01;2013-10-1;2013—10—01;2013年8月31日;2013-10-31;2013-09-30;2013年10月31日;10/31/2013;11/30/2310;09-01;2013年10月1日;9.30;2013-10-31;2013-10-30;2013/11/01;Nov-13;2013-011-30;09-31;201310-01;09-30;2013-11-01;12/1/2013;2013-10-301;2013-11-1;2013-10-1;013.10.01;2013-11-01;2013-10-2;2013-10-3;11/1/2013;8月31日;11/1/2011;2013-09-30;2013-9-30;2013-10-01;2013-10-0;2013-09-01;08-31;2013-09-02;11-30;2013-10-32;2013-10-31;2013-10-33;9.1;2013-10-34;10/1/2013;9/30/2013;10/30/2013;12/31/2013;2013-11-31;201309-01;2013-11-30;201310-1;8月1日;2/3/1901;3013-09-01;2013年09月01日;013.10.31;9/1/2013;2013年9月1日;2013年8月1日;2013-09-01;2013-09-30;2013-09-31;2013-9-1;2013年11月30日;10.31;2013-01;2013-10-61;2013.10.01;10-31;2013-11-30;2013—10—31;Saturday, November 30, 2013;201311-01;2013-11 -01;2013年10月01日;2013-10-01;2013年11月1日;201310-31
那么多乱七八糟的格式要写多少正则表达式啊。表达式写错了又该怎么查出来。还有正则表达式该怎么读取到其上层节点的数据做出判断呢?转念一想,这不是XPath应该干的活么?我们最终抛弃了正则表达式这个好帮手,找到了VTD来解析XML(我问过度娘和谷哥,VTD这个技术貌似使用的范围很小。有时候在怀疑是不是只剩下我们公司自己在用这个了)。
首先将所有出现的格式异常和其对应的修改方案构建一个Map对象(key为出现的错误日期格式,value为正确的格式)。这种做法很挫但是很直接。
Stringsource = "Sunday, September 01,2013:2013-09-01;2013-11-1:2013-11-01;11/30/2013:2013-11-30;11/1/2013:2013-11-01;";
String[]sourceArray = source.split(";"),temp;
HashMapmodifyer=new HashMap();
for(Strings:sourceArray){
temp=s.split(":");
modifyer.put(temp[0],temp[1]);
}
替换的代码为
try{
String sourceXML =SerializeUtil.ClobToString(returnValue);
VTDGenvg = new VTDGen();
vg.setDoc(sourceXML.getBytes("UTF-8"));
vg.parse(true);
VTDNavvn = vg.getNav();
AutoPilotap = new AutoPilot(vn);
XMLModifierxm = new XMLModifier(vn);
ap.selectXPath("//parentNode[condiction='xxxx']/childNode");
while(ap.evalXPath()> -1){
error= vn.toString(vn.getText());
right= (String)modifyer.get(vn.toString(vn.getText()));
if(right!=null){
xm.updateToken(vn.getText(),right);
ErrorLog.append(dataId+""+"出现"+error+"类型错误,已更改为"+right+";\n");
}
ByteArrayOutputStreamos = new ByteArrayOutputStream();
xm.output(os);
sourceXML=os.toString();
os.flush();
os.close();
byte[]xx = SerializeUtil.zipBytes(sourceXML.getBytes());
HashMap<String,Object>map1 =newHashMap<String,Object>();
map1.put("DATA",xx);
map1.put("DATAID", dataId);
map1.put("FINISHED", "Y");
session.update("org.mybatis.example.ChangXmlMapper.updateBloB",map1);
session.commit();
}
}catch(Exception e) {
e.printStackTrace();
}
正当以为大功的时候,VTD报了
at com.ximpleware.XMLModifier.quickSort(XMLModifier.java:1965)
我们查看了一下报文。发现这是一个大数据报文,存为XML文件都占了几兆空间。应该是VTD在生成XML对象时出现了JVM内存不足。这是个硬伤啊,我那个小破本已经到极限了还是报内存不足。好在,大数据的报文不多,先把大数据的条目跳过后面再手动处理吧。
手动处理大数据报文就只能靠工具了。我用的是Editplus(有用过Editplus处理过上百兆的SQL语句,速度还不错)。用异常格式查找替换为正确的格式,相当吐血。然后再用Java读取文件生成字节流update回去。已经没血可吐了。
dump导出修改后的表,发邮件出去。打完收工。但愿不要再出这种问题了。这里写下来只是为了留念。绝对没有再让这程序跑的意思。午夜下班的程序猿伤不起啊。