Java基于正则表达式实现xml文件的解析功能详解

发布时间 - 2026-01-11 02:56:49    点击率:

本文实例讲述了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./regex/javascript

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

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

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


# Java  # 正则表达式  # xml文件  # 解析  # Java对XML文件增删改查操作示例  # java 解析由String类型拼接的XML文件方法  # java实现简单解析XML文件功能示例  # JAVA中JSONObject对象和Map对象之间的相互转换  # 详解Java中String JSONObject JSONArray List<实体类>  # java实现Xml与json之间的相互转换操作示例  # Java实现Json字符串与Object对象相互转换的方式总结  # Java解析xml文件和json转换的方法(DOM4j解析)  # 递归  # 文档  # 以对  # 多条  # 不包含  # 都是  # 操作技巧  # 相关内容  # 不需要  # 都能  # 感兴趣  # 数据结构  # 可以通过  # 给人  # 这是我  # 给大家  # 不存在  # 考虑到  # 自定义 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点  JavaScript常见的五种数组去重的方式  网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?  如何快速搭建虚拟主机网站?新手必看指南  如何快速生成ASP一键建站模板并优化安全性?  Laravel如何实现多对多模型关联?(Eloquent教程)  如何在阿里云完成域名注册与建站?  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  zabbix利用python脚本发送报警邮件的方法  edge浏览器无法安装扩展 edge浏览器插件安装失败【解决方法】  Python图片处理进阶教程_Pillow滤镜与图像增强  如何用美橙互联一键搭建多站合一网站?  如何用低价快速搭建高质量网站?  微信小程序 配置文件详细介绍  Laravel如何使用Eloquent ORM进行数据库操作?(CRUD示例)  如何在云指建站中生成FTP站点?  如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南  如何选择可靠的免备案建站服务器?  nodejs redis 发布订阅机制封装实现方法及实例代码  夸克浏览器网页跳转延迟怎么办 夸克浏览器跳转优化  Laravel如何实现API版本控制_Laravel API版本化路由设计策略  JS实现鼠标移上去显示图片或微信二维码  Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康  如何使用 jQuery 正确渲染 Instagram 风格的标签列表  laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法  高防服务器租用如何选择配置与防御等级?  如何在建站主机中优化服务器配置?  Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全  如何在腾讯云服务器快速搭建个人网站?  新三国志曹操传主线渭水交兵攻略  EditPlus中的正则表达式 实战(4)  Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程  Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件  Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出  Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?  Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】  历史网站制作软件,华为如何找回被删除的网站?  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  晋江文学城电脑版官网 晋江文学城网页版直接进入  JavaScript如何实现错误处理_try...catch如何捕获异常?  利用 Google AI 进行 YouTube 视频 SEO 描述优化  如何基于云服务器快速搭建个人网站?  香港服务器如何优化才能显著提升网站加载速度?  Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程  Laravel怎么为数据库表字段添加索引以优化查询  网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?  node.js报错:Cannot find module &#39;ejs&#39;的解决办法  Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】