Java基于正则表达式实现xml文件的解析功能详解
本文实例讲述了java基于正则表达式实现xml文件的解析功能。分享给大家供大家参考,具体如下:
这是我通过正则表达式实现的xml文件解析工具,有些xhtml文件中包含特殊符号,暂时还无法正常使用。
设计思路:常见的xml文件都是单根树结构,工具的目的是通过递归的方式将整个文档树装载进一个node对象。xml文档树上的每一个节点都能看做一个node对象,它拥有title、attribute和text三个自身变量以及一个childrennode集合用来存放子节点,使用正则表达式完整装载。
一、编写node类
node对象是文档解析的基础,最终可以通过对象的不同属性实现对文档信息的访问。
node.java:
import java.io.serializable; import java.util.hashmap; import java.util.iterator; import java.util.linkedlist; import java.util.list; import java.util.map; import java.util.map.entry; public class node implements serializable { // 可以对node对象持久化保存 private static final long serialversionuid = 1l; private int id; // 节点类型 private string title; // 节点内容 private string text; // 节点属性集合 private map<string, string> attributes = new hashmap<string, string>(); // 子节点集合 private list<node> childnodes = new linkedlist<node>(); public int getid() { return id; } public void setid(int id) { this.id = id; } public string gettitle() { return title; } public void settitle(string title) { this.title = title; } public map<string, string> getattribute() { return attributes; } public void setattribute(map<string, string> attribute) { this.attributes = attribute; } public string gettext() { return text; } public void settext(string text) { this.text = text; } public list<node> getchildnode() { return childnodes; } public void setchildnode(list<node> childnode) { this.childnodes = childnode; } // 将属性集合转换成一条完整的字符串 private string attrtostring() { if (attributes.isempty()) { return ""; } iterator<entry<string, string>> its = attributes.entryset().iterator(); stringbuffer buff = new stringbuffer(); while (its.hasnext()) { entry<string, string> entry = its.next(); buff.append(entry.getkey() + "=\"" + entry.getvalue() + "\" "); } return " " + buff.tostring().trim(); } // 输出完整的节点字符串也用到了递归 @override public string tostring() { string attr = attrtostring(); if (childnodes.isempty() && text == null) { return "<" + title + attr + "/>\n"; } else if (childnodes.isempty() && text != null) { return "<" + title + attr + ">\n" + text + "\n" + "</" + title + ">\n"; } else { stringbuffer buff = new stringbuffer(); buff.append("<" + title + attr + ">\n"); if (!text.isempty()) { buff.append(text + "\n"); } for (node n : childnodes) { buff.append(n.tostring()); } buff.append("</" + title + ">\n"); return buff.tostring(); } } }
二、创建接口
把文档的读取和分析抽象成接口方便今后替换实现。
过滤器:读取文档的字符流并删除注释的部分。这些信息通常是提供给人阅读的,程序分析直接忽略。
xmlfilter.java:
/* * 过滤器的作用是删除xml文件中不重要的部分。 * 通常都是一些注释性文字,不需要被机器解析。 */ public interface xmlfilter { string filter(); // 提供自定义正则表达式,识别符合过滤条件的字符串 string filter(string[] regex); }
解析器:将一个父节点解析成多条子节点的字符串。如果返回值为null,代表当前节点下不存在可以继续解析的对象。
xmlparser.java:
import java.util.list; /* * 解析器可以对一段完整的父节点字符串提供解析服务。 * 将一条父节点的字符串解析成为多条子节点字符串 */ public interface xmlparser { // 解析一段父节点,返回子节点字符串 list<string> parser(string str); }
三、根据接口编写实现类
回车、换行、制表符以及各种注释部分的内容都被删除,简化字符输出。
simplexmlfilter.java:
import java.io.bufferedreader; import java.io.file; import java.io.filenotfoundexception; import java.io.filereader; import java.io.ioexception; public class simplexmlfilter implements xmlfilter { private string text; // 常用的过滤正则表达式 public final static string[] reg = { "\t", "<\\?.*?\\?>", "<!.*?>", "<%.*?%>", "\\s{2,}" }; // 读取xml文档返回字符串 public simplexmlfilter(file file) throws ioexception { bufferedreader in = new bufferedreader(new filereader(file)); stringbuffer buff = new stringbuffer(); string temp = null; while ((temp = in.readline()) != null) { buff.append(temp); } in.close(); text = buff.tostring().trim(); } @override public string filter() { return filter(reg); } @override public string filter(string[] regex) { string result = text; for (string reg : regex) { result = result.replaceall(reg, ""); } return result; } }
主要是通过正则表达式区分一个节点内部的子节点,考虑到节点的类型我将它们分为自闭合与非自闭合两种类型。<title attributes .../>这样的节点属于自闭合类型,它们不包含子节点和text属性,它们属于文档树的叶子节点。<title attributes ...>text ...</title>这样的节点属于非自闭合类型,它们属于文档树的分支节点。
simplexmlparser.java:
import java.util.arraylist; import java.util.list; import java.util.regex.matcher; import java.util.regex.pattern; public class simplexmlparser implements xmlparser { @override public list<string> parser(string text) { list<string> childrendocs = new arraylist<string>(); // 捕获根节点中间的文本 pattern p = pattern.compile("<.*?>(.*)</.*?>"); matcher m = p.matcher(text); if (m.matches()) { string inner = m.group(1); // 匹配节点字符串 p = pattern.compile("<(.*?)>"); m = p.matcher(inner); while (m.find()) { string s1 = m.group(1); // 如果节点以/结尾,代表此节点不包含子节点 if (s1.endswith("/")) { childrendocs.add(m.group()); // 如果节点既不以/开头,也不以/结尾则表示需要查找对应的闭合节点 } else if (!s1.startswith("/") && !s1.endswith("/")) { // 计算起始字符数 int start = m.end() - m.group().length(); // 如果捕获到未闭合节点则index++,如果捕获到闭合节点则index-- int index = 1; while (m.find()) { string s2 = m.group(1); if (!s2.startswith("/") && !s2.endswith("/")) { index++; } else if (s2.startswith("/")) { index--; } // 找到符合条件的闭合节点则循环终止 if (index == 0) { break; } } // 计算结束字符数 int end = m.end(); // 截取对应字符串 childrendocs.add(inner.substring(start, end)); } } } return childrendocs; } }
四、编写nodebuilder类
根据过滤器和解析器获取node节点各属性的值。
nodebuilder.java:
import java.io.file; import java.io.ioexception; import java.util.list; import java.util.regex.matcher; import java.util.regex.pattern; // 生成node public class nodebuilder { private node root = new node(); private xmlparser parser; private xmlfilter filter; // 提供合适的过滤器和解析器 public nodebuilder(xmlparser parser, xmlfilter filter) { this.parser = parser; this.filter = filter; } public node getroot(string... regex) { string str = null; if (regex.length == 0) { str = filter.filter(); } else { str = filter.filter(regex); } buildnodetree(str, root); return root; } // 设置节点类型 private void buildnodetitle(string str, node n) { pattern p = pattern.compile("<.*?>"); matcher m = p.matcher(str); if (m.find()) { string temp = m.group(); string s = 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]); } } } // 设置节点属性集合 private void buildnodeattribute(string str, node n) { pattern p = pattern.compile("<.*?>"); matcher m = p.matcher(str); if (m.find()) { string temp = m.group(); string s = temp.substring(1, temp.length() - 1); // 匹配字符串 p = pattern.compile("(\\s*)=\"(.*?)\""); m = p.matcher(s); while (m.find()) { string key = m.group(1).trim(); string value = m.group(2).trim(); n.getattribute().put(key, value); } // 匹配数字 p = pattern.compile("(\\s*)=(-?\\d+(\\.\\d+)?)"); m = p.matcher(s); while (m.find()) { string key = m.group(1).trim(); string value = m.group(2).trim(); n.getattribute().put(key, value); } } } // 设置节点内容,节点的内容是删除了所有子节点字符串以后剩下的部分 private void buildnodetext(string str, node n) { pattern p = pattern.compile("<.*?>(.*)</.*?>"); matcher m = p.matcher(str); list<string> childrendocs = parser.parser(str); if (m.find()) { string temp = m.group(1); for (string s : childrendocs) { temp = temp.replaceall(s, ""); } n.settext(temp.trim()); } } // 通过递归生成完整节点树 private void buildnodetree(string str, node n) { buildnodetitle(str, n); buildnodeattribute(str, n); buildnodetext(str, n); // 如果存在子节点则继续下面的操作 if (!parser.parser(str).isempty()) { // 对每一个子节点都应该继续调用直到递归结束 for (string temp : parser.parser(str)) { node child = new node(); buildnodetitle(temp, child); buildnodeattribute(temp, child); buildnodetext(temp, child); n.getchildnode().add(child); buildnodetree(temp, child); } } } }
五、测试
编写xml测试文件
测试文件:
<package> <!-- 这里是注释1 --> package message before! <class id="exp1" path="www.sina.com"/> <class id="exp2"> <class id="inner"> class message inner. </class> </class> package message middle! <!-- 这里是注释2 --> <class id="exp3"> <method id="md" name="setter" order=1> <!-- 这里是注释3 --> <!-- 这里是注释4 --> <para ref="string"/> <para ref="exp1"> method message inner! </para> </method> </class> package message after! </package>
编写测试类
demo.java:
import java.io.file; import java.io.ioexception; public class demo { public static void main(string[] args) { file f = new file("xxx"); xmlfilter filter = null; try { filter = new simplexmlfilter(f); } catch (ioexception e) { e.printstacktrace(); } xmlparser parser = new simplexmlparser(); nodebuilder builder = new nodebuilder(parser, filter); node node = builder.getroot(); system.out.println(node); } }
输出:
<package> package message before!package message middle!package message after! <class path="www.sina.com" id="exp1"/> <class id="exp2"> <class id="inner"> class message inner. </class> </class> <class id="exp3"> <method name="setter" id="md" order="1"> <para ref="string"/> <para ref="exp1"> method message inner! </para> </method> </class> </package>
ps:这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:
javascript正则表达式在线测试工具:
正则表达式在线生成工具:
更多关于java算法相关内容感兴趣的读者可查看本站专题:《java正则表达式技巧大全》、《java数据结构与算法教程》、《java操作dom节点技巧总结》、《java文件与目录操作技巧汇总》和《java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。
上一篇: java异步上传图片示例