java正则表达式 读取xml文件_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 attributes = new HashMap();

// 子节点集合

private List childNodes = new LinkedList();

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 getAttribute() {

return attributes;

}

public void setAttribute(Map attribute) {

this.attributes = attribute;

}

public String getText() {

return text;

}

public void setText(String text) {

this.text = text;

}

public List getChildNode() {

return childNodes;

}

public void setChildNode(List childNode) {

this.childNodes = childNode;

}

// 将属性集合转换成一条完整的字符串

private String attrToString() {

if (attributes.isEmpty()) {

return "";

}

Iterator> its = attributes.entrySet().iterator();

StringBuffer buff = new StringBuffer();

while (its.hasNext()) {

Entry 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 "\n";

} else if (childNodes.isEmpty() && text != null) {

return "\n" + text + "\n" + "" + title + ">\n";

} else {

StringBuffer buff = new StringBuffer();

buff.append("\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 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;

}

}

主要是通过正则表达式区分一个节点内部的子节点,考虑到节点的类型我将它们分为自闭合与非自闭合两种类型。

这样的节点属于自闭合类型,它们不包含子节点和text属性,它们属于文档树的叶子节点。text ...这样的节点属于非自闭合类型,它们属于文档树的分支节点。

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 parser(String text) {

List childrenDocs = new ArrayList();

// 捕获根节点中间的文本

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 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 message before!

class message inner.

package message middle!

method message inner!

package message after!

编写测试类

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 message before!package message middle!package message after!

class message inner.

method message inner!

PS:这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:

希望本文所述对大家java程序设计有所帮助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值