Java基于正则表达式实现xml文件的解析功能详解
本文实例讲述了Java基于正则表达式实现xml文件的解析功能。分享给大家供大家参考,具体如下:
这是我通过正则表达式实现的xml文件解析工具,有些XHTML文件中包含特殊符号,暂时还无法正常使用。
设计思路:常见的xml文件都是单根树结构,工具的目的是通过递归的方式将整个文档树装载进一个Node对象。xml文档树上的每一个节点都能看做一个Node对象,它拥有title、attribute和text三个自身变量以及一个childrenNode集合用来存放子节点,使用正则表达式完整装载。
一、编写Node类
Node对象是文档解析的基础,最终可以通过对象的不同属性实现对文档信息的访问。
Node.java:
importjava.io.Serializable; importjava.util.HashMap; importjava.util.Iterator; importjava.util.LinkedList; importjava.util.List; importjava.util.Map; importjava.util.Map.Entry; publicclassNodeimplementsSerializable{ //可以对Node对象持久化保存 privatestaticfinallongserialVersionUID=1L; privateintid; //节点类型 privateStringtitle; //节点内容 privateStringtext; //节点属性集合 privateMapattributes=newHashMap (); //子节点集合 privateList childNodes=newLinkedList (); publicintgetId(){ returnid; } publicvoidsetId(intid){ this.id=id; } publicStringgetTitle(){ returntitle; } publicvoidsetTitle(Stringtitle){ this.title=title; } publicMap getAttribute(){ returnattributes; } publicvoidsetAttribute(Map attribute){ this.attributes=attribute; } publicStringgetText(){ returntext; } publicvoidsetText(Stringtext){ this.text=text; } publicList getChildNode(){ returnchildNodes; } publicvoidsetChildNode(List childNode){ this.childNodes=childNode; } //将属性集合转换成一条完整的字符串 privateStringattrToString(){ if(attributes.isEmpty()){ return""; } Iterator >its=attributes.entrySet().iterator(); StringBufferbuff=newStringBuffer(); while(its.hasNext()){ Entry entry=its.next(); buff.append(entry.getKey()+"=\""+entry.getValue()+"\""); } return""+buff.toString().trim(); } //输出完整的节点字符串也用到了递归 @Override publicStringtoString(){ Stringattr=attrToString(); if(childNodes.isEmpty()&&text==null){ return"<"+title+attr+"/>\n"; }elseif(childNodes.isEmpty()&&text!=null){ return"<"+title+attr+">\n"+text+"\n"+""+title+">\n"; }else{ StringBufferbuff=newStringBuffer(); buff.append("<"+title+attr+">\n"); if(!text.isEmpty()){ buff.append(text+"\n"); } for(Noden:childNodes){ buff.append(n.toString()); } buff.append(""+title+">\n"); returnbuff.toString(); } } }
二、创建接口
把文档的读取和分析抽象成接口方便今后替换实现。
过滤器:读取文档的字符流并删除注释的部分。这些信息通常是提供给人阅读的,程序分析直接忽略。
XmlFilter.java:
/* *过滤器的作用是删除xml文件中不重要的部分。 *通常都是一些注释性文字,不需要被机器解析。 */ publicinterfaceXmlFilter{ Stringfilter(); //提供自定义正则表达式,识别符合过滤条件的字符串 Stringfilter(String[]regex); }
解析器:将一个父节点解析成多条子节点的字符串。如果返回值为null,代表当前节点下不存在可以继续解析的对象。
XmlParser.java:
importjava.util.List; /* *解析器可以对一段完整的父节点字符串提供解析服务。 *将一条父节点的字符串解析成为多条子节点字符串 */ publicinterfaceXmlParser{ //解析一段父节点,返回子节点字符串 Listparser(Stringstr); }
三、根据接口编写实现类
回车、换行、制表符以及各种注释部分的内容都被删除,简化字符输出。
SimpleXmlFilter.java:
importjava.io.BufferedReader; importjava.io.File; importjava.io.FileNotFoundException; importjava.io.FileReader; importjava.io.IOException; publicclassSimpleXmlFilterimplementsXmlFilter{ privateStringtext; //常用的过滤正则表达式 publicfinalstaticString[]REG={"\t","<\\?.*?\\?>","","<%.*?%>","\\s{2,}"}; //读取xml文档返回字符串 publicSimpleXmlFilter(Filefile)throwsIOException{ BufferedReaderin=newBufferedReader(newFileReader(file)); StringBufferbuff=newStringBuffer(); Stringtemp=null; while((temp=in.readLine())!=null){ buff.append(temp); } in.close(); text=buff.toString().trim(); } @Override publicStringfilter(){ returnfilter(REG); } @Override publicStringfilter(String[]regex){ Stringresult=text; for(Stringreg:regex){ result=result.replaceAll(reg,""); } returnresult; } }
主要是通过正则表达式区分一个节点内部的子节点,考虑到节点的类型我将它们分为自闭合与非自闭合两种类型。
SimpleXmlParser.java:
importjava.util.ArrayList; importjava.util.List; importjava.util.regex.Matcher; importjava.util.regex.Pattern; publicclassSimpleXmlParserimplementsXmlParser{ @Override publicListparser(Stringtext){ List childrenDocs=newArrayList (); //捕获根节点中间的文本 Patternp=Pattern.compile("<.*?>(.*)"); Matcherm=p.matcher(text); if(m.matches()){ Stringinner=m.group(1); //匹配节点字符串 p=Pattern.compile("<(.*?)>"); m=p.matcher(inner); while(m.find()){ Strings1=m.group(1); //如果节点以/结尾,代表此节点不包含子节点 if(s1.endsWith("/")){ childrenDocs.add(m.group()); //如果节点既不以/开头,也不以/结尾则表示需要查找对应的闭合节点 }elseif(!s1.startsWith("/")&&!s1.endsWith("/")){ //计算起始字符数 intstart=m.end()-m.group().length(); //如果捕获到未闭合节点则index++,如果捕获到闭合节点则index-- intindex=1; while(m.find()){ Strings2=m.group(1); if(!s2.startsWith("/")&&!s2.endsWith("/")){ index++; }elseif(s2.startsWith("/")){ index--; } //找到符合条件的闭合节点则循环终止 if(index==0){ break; } } //计算结束字符数 intend=m.end(); //截取对应字符串 childrenDocs.add(inner.substring(start,end)); } } } returnchildrenDocs; } }
四、编写NodeBuilder类
根据过滤器和解析器获取Node节点各属性的值。
NodeBuilder.java:
importjava.io.File; importjava.io.IOException; importjava.util.List; importjava.util.regex.Matcher; importjava.util.regex.Pattern; //生成Node publicclassNodeBuilder{ privateNoderoot=newNode(); privateXmlParserparser; privateXmlFilterfilter; //提供合适的过滤器和解析器 publicNodeBuilder(XmlParserparser,XmlFilterfilter){ this.parser=parser; this.filter=filter; } publicNodegetRoot(String...regex){ Stringstr=null; if(regex.length==0){ str=filter.filter(); }else{ str=filter.filter(regex); } buildNodeTree(str,root); returnroot; } //设置节点类型 privatevoidbuildNodeTitle(Stringstr,Noden){ Patternp=Pattern.compile("<.*?>"); Matcherm=p.matcher(str); if(m.find()){ Stringtemp=m.group(); Strings=temp.substring(1,temp.length()-1).split("")[0]; if(s.endsWith("/")){ n.setTitle(s.substring(0,s.length()-1)); }else{ n.setTitle(s.split("")[0]); } } } //设置节点属性集合 privatevoidbuildNodeAttribute(Stringstr,Noden){ Patternp=Pattern.compile("<.*?>"); Matcherm=p.matcher(str); if(m.find()){ Stringtemp=m.group(); Strings=temp.substring(1,temp.length()-1); //匹配字符串 p=Pattern.compile("(\\S*)=\"(.*?)\""); m=p.matcher(s); while(m.find()){ Stringkey=m.group(1).trim(); Stringvalue=m.group(2).trim(); n.getAttribute().put(key,value); } //匹配数字 p=Pattern.compile("(\\S*)=(-?\\d+(\\.\\d+)?)"); m=p.matcher(s); while(m.find()){ Stringkey=m.group(1).trim(); Stringvalue=m.group(2).trim(); n.getAttribute().put(key,value); } } } //设置节点内容,节点的内容是删除了所有子节点字符串以后剩下的部分 privatevoidbuildNodeText(Stringstr,Noden){ Patternp=Pattern.compile("<.*?>(.*)"); Matcherm=p.matcher(str); ListchildrenDocs=parser.parser(str); if(m.find()){ Stringtemp=m.group(1); for(Strings:childrenDocs){ temp=temp.replaceAll(s,""); } n.setText(temp.trim()); } } //通过递归生成完整节点树 privatevoidbuildNodeTree(Stringstr,Noden){ buildNodeTitle(str,n); buildNodeAttribute(str,n); buildNodeText(str,n); //如果存在子节点则继续下面的操作 if(!parser.parser(str).isEmpty()){ //对每一个子节点都应该继续调用直到递归结束 for(Stringtemp:parser.parser(str)){ Nodechild=newNode(); buildNodeTitle(temp,child); buildNodeAttribute(temp,child); buildNodeText(temp,child); n.getChildNode().add(child); buildNodeTree(temp,child); } } } }
五、测试
编写xml测试文件
测试文件:
packagemessagebefore! classmessageinner. packagemessagemiddle! methodmessageinner! packagemessageafter!
编写测试类
Demo.java:
importjava.io.File; importjava.io.IOException; publicclassDemo{ publicstaticvoidmain(String[]args){ Filef=newFile("xxx"); XmlFilterfilter=null; try{ filter=newSimpleXmlFilter(f); }catch(IOExceptione){ e.printStackTrace(); } XmlParserparser=newSimpleXmlParser(); NodeBuilderbuilder=newNodeBuilder(parser,filter); Nodenode=builder.getRoot(); System.out.println(node); } }
输出:
packagemessagebefore!packagemessagemiddle!packagemessageafter! classmessageinner. methodmessageinner!
PS:这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:
JavaScript正则表达式在线测试工具:
http://tools.jb51.net/regex/javascript
正则表达式在线生成工具:
http://tools.jb51.net/regex/create_reg
更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java正则表达式技巧大全》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。