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正则表达式在线测试工具:
http://tools.jb51.net/regex/javascript

正则表达式在线生成工具:
http://tools.jb51.net/regex/create_reg

更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java正则表达式技巧大全》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

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

时间: 2017-08-23

java中使用sax解析xml的解决方法

在java中,原生解析xml文档的方式有两种,分别是:Dom解析和Sax解析 Dom解析功能强大,可增删改查,操作时会将xml文档以文档对象的方式读取到内存中,因此适用于小文档 Sax解析是从头到尾逐行逐个元素读取内容,修改较为不便,但适用于只读的大文档 本文主要讲解Sax解析,其余放在后面 Sax采用事件驱动的方式解析文档.简单点说,如同在电影院看电影一样,从头到尾看一遍就完了,不能回退(Dom可来来回回读取) 在看电影的过程中,每遇到一个情节,一段泪水,一次擦肩,你都会调动大脑和神经去接收或

Java如何读取XML文件 具体实现

今天的CSDN常见问题来讲解下在Java中如何读取XML文件的内容. 直接上代码吧,注释写的很清楚了! 复制代码 代码如下: import java.io.*;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;im

java读取解析xml文件实例

读取本地的xml文件,通过DOM进行解析,DOM解析的特点就是把整个xml文件装载入内存中,形成一颗DOM树形结构,树结构是方便遍历和和操纵. DOM解析的特性就是读取xml文件转换为 dom树形结构,通过节点进行遍历. 这是W3c关于节点的概念 如果xml中包含有大量的数据,由于dom一次性把xml装入内存中的特性,所以dom不适合于包含大量数据的xml解析.当包含有大量xml的时候,用SAX进行解析比较节省内存. 下面是一个运用DOM进行解析xml文件的例子: xml文件结构如下: <?xm

java操作(DOM、SAX、JDOM、DOM4J)xml方式的四种比较与详解

1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作.由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的.DOM以及广义的基于树的处理具有几个优点.首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改.它还可以在任何时候在树中上下导航,而不是像SAX那

java使用xpath解析xml示例分享

XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言.XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力.起初 XPath 的提出的初衷是将其作为一个通用的.介于XPointer与XSL间的语法模型.但是 XPath 很快的被开发者采用来当作小型查询语言. XPathTest.java 复制代码 代码如下: package com.hongyuan.test; import java.io.File;import java

Java生成和解析XML格式文件和字符串的实例代码

1.基础知识: Java解析XML一般有四种方法:DOM.SAX.JDOM.DOM4J. 2.使用介绍 1).DOM (1)简介 由W3C(org.w3c.dom)提供的接口,它将整个XML文档读入内存,构建一个DOM树来对各个节点(Node)进行操作.优点就是整个文档都一直在内存中,我们可以随时访问任何节点,并且对树的遍历也是比较熟悉的操作:缺点则是耗内存,并且必须等到所有的文档都读入内存才能进行处理. (2)示例代码: 复制代码 代码如下: <?xml version="1.0&quo

java解析XML几种方式小结

java解析XML几种方式小结 第一种:DOM. DOM的全称是Document Object Model,也即文档对象模型.在应用程序中,基于DOM的XML分析器将一个XML文档转换成一个对象模型的集合(通常称DOM树),应用程序正是通过对这个对象模型的操作,来实现对XML文档数据的操作.通过DOM接口,应用程序可以在任何时候访问XML文档中的任何一部分数据,因此,这种利用DOM接口的机制也被称作随机访问机制. DOM接口提供了一种通过分层对象模型来访问XML文档信息的方式,这些分层对象模型依

Java使用正则表达式提取XML节点内容的方法示例

本文实例讲述了Java使用正则表达式提取XML节点内容的方法.分享给大家供大家参考,具体如下: 现在有类似<doc>abc</doc><title>3232</title> <doc>只要内容</doc>这么一个串,需要提取abc,3232,只要内容的节点内容 public static List getContext(String html) { List resultList = new ArrayList(); Pattern

Java使用正则表达式截取重复出现的XML字符串功能示例

本文实例讲述了Java使用正则表达式截取重复出现的XML字符串功能.分享给大家供大家参考,具体如下: public static void main(String[] args) throws DocumentException { String s = "<MTCCommand Time=\"2014-03-22\"><Login UserId=\"admin\" UserPasswd=\"123\" />&l

java正则表达式验证邮箱、电话号码示例

下面的代码使用正则表达式验证输入格式包括了验证邮箱和验证手机号码 复制代码 代码如下: package com.firewolf.utils; import java.util.regex.Matcher; import java.util.regex.Pattern; /**  * 使用正则表达式验证输入格式  * @author liuxing  *  */ public class RegexValidateUtil {  public static void main(String[]

java中 利用正则表达式提取( )内内容

昨天遇到一个小问题,需要批量处理一些用户,而前台传来的用户格式如下,要提取括号中间的内容(不带括号) 教师10(0010)教师11(0011)教师9(009)教师12(0012)教师13(0013)教师14(0014) 本来想用java的String.split()和substring()来搞定,但是需要处理多次比较麻烦,就用正则表达式了.虽然语法忘得差不多了,但是印象中用断言比较方便(关键希望结果不带括号).打开RegexBuddy试了下,轻松搞定:下边是java实现代码: 复制代码 代码如下

Java利用正则表达式提取数据的方法

什么是正则表达式 正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. Java利用正则表达式提取数据 Java正则表达式的用途很广,之前要用到将一大 3M 的 txt 文本切分成多个小文本,用 C# 写的话很简洁,代码也就二十几行,今天用 Java 写了一下,果然,Java 很罗嗦. 切分文件的代码

java中利用反射调用另一类的private方法的简单实例

我们知道,Java应用程序不能访问持久化类的private方法,但Hibernate没有这个限制,它能够访问各种级别的方法,如private, default, protected, public. Hibernate是如何实现该功能的呢?答案是利用JAVA的反射机制,如下: import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectDemo {

SQL Server中利用正则表达式替换字符串的方法

建立正则替换函数,利用了OLE对象,以下是函数代码: --如果存在则删除原有函数 IF OBJECT_ID(N'dbo.RegexReplace') IS NOT NULL DROP FUNCTION dbo.RegexReplace GO --开始创建正则替换函数 CREATE FUNCTION dbo.RegexReplace ( @string VARCHAR(MAX), --被替换的字符串 @pattern VARCHAR(255), --替换模板 @replacestr VARCHAR

Java中使用正则表达式处理文本数据

本文将介绍如何在Java中使用正则表达式来处理文本数据.正则表达式就是一个字符串,但和普通的字符串不同的是,正则表达式是对一组相似字符串的抽象,如下面的几个字符串: a98b   c0912d   c10b   a12345678d   ab 我们仔细分析上面五个字符串,可以看出它们有一个共同特征,就是第一个字符必须是'a'或'c',最后一个字符必须是'b'或'd',而中间的字符是任意多个数字组成(包括0个数字).因此,我们可以将这五个字符串的共同特点抽象出来,这就产生了一个正则表达式:[ac]

Java 中利用泛型和反射机制抽象DAO的实例

Java 中利用泛型和反射机制抽象DAO的实例 一般的DAO都有CRUD操作,在每个实体DAO接口中重复定义这些方法,不如提供一个通用的DAO接口,具体的实体DAO可以扩展这个通用DAO以提供特殊的操作,从而将DAO抽象到另一层次,令代码质量有很好的提升 1.通用接口 import java.io.Serializable; import java.util.List; public interface BaseDao<T> { T get(Serializable id); List<

js利用正则表达式检验输入内容是否为网址

js正则检验输入的是否为网址功能在网页中也是很常见的,友情链接部分.表单填写个人主页的时候,使用JavaScript取验证是否为网址. 这个检验不好写,最好还是使用正则表达式去认证. 规定,输入的东西只能是http://与https://开头,而且必须是网址. 有人说,为何像www.1.com这样的网页不行呢? 这是以免你拿用户输入的东西构造超级链接的时候,a标签中的href属性如果遇不到http://或者https://的东西,那么就会认为是根目录,会在你的网站的网址后面接着写入这个地址再跳转

java中利用Dom4j解析和生成XML文档

一.前言 dom4j是一套非常优秀的Java开源api,主要用于读写xml文档,具有性能优异.功能强大.和非常方便使用的特点.   另外xml经常用于数据交换的载体,像调用webservice传递的参数,以及数据做同步操作等等,   所以使用dom4j解析xml是非常有必要的. 二.准备条件 dom4j.jar 下载地址:http://sourceforge.net/projects/dom4j/ 三.使用Dom4j实战 1.解析xml文档 实现思路: <1>根据读取的xml路径,传递给SAX

python利用正则表达式提取字符串

前言 正则表达式的基础知识就不说了,有兴趣的可以点击这里,提取一般分两种情况,一种是提取在文本中提取单个位置的字符串,另一种是提取连续多个位置的字符串.日志分析会遇到这种情况,下面我会分别讲一下对应的方法. 一.单个位置的字符串提取 这种情况我们可以使用(.+?)这个正则表达式来提取. 举例,一个字符串"a123b",如果我们想提取ab之间的值123,可以使用findall配合正则表达式,这样会返回一个包含所以符合情况的list. 代码如下: import re str = "

PHP中使用正则表达式提取中文实现笔记

最近老板叫做一个数据查重的小练习,涉及从一个包含中文字段的文件中提取出其中的中文字段并存储,使用php开发.中间涉及到php正则表达式中文匹配的问题,网上搜罗一大片,但是也很乱没有一个准信儿,经过自己的代码的修改和检验,先将extract函数写下. 首先要注意到的是,双字节字符的编码问题,这里我们以后还可能会遇到像韩文.日文等编码问题,与中文理解上是一个意思. 1. GBK (GB2312/GB18030) 复制代码 代码如下: \x00-\xff GBK双字节编码范围   \x20-\x7f