Excel文件本质是XML文件,对应两种解析方式
- SAX 流式读取,对应 XSSFReader
- DOM 树读取,对应 Workbook
很好的问题!让我用通俗易懂的方式解释 SAX 和 DOM 这两种 XML 解析模式。
核心概念
XML 解析器读取 XML 文件时有两种基本策略:
- SAX:边读边处理(流式)
- DOM:全部读入后再处理(树状)
SAX (Simple API for XML)
工作原理
XML 文件: → 解析器 → 事件触发 → 你的代码处理
<root> ↓ ↓
<a>1</a> 开始读取 "遇到开始标签 <a>"
<b>2</b> 继续读取 "遇到开始标签 <b>"
</root> ↓ ↓
继续读取 "遇到内容 2"
"遇到结束标签 </b>"
"遇到结束标签 </a>"
...
代码示例
java
// SAX 解析器会回调这些方法
public class MyHandler extends DefaultHandler {
@Override
public void startElement(String uri, String name, String qName, Attributes atts) {
System.out.println("开始标签: " + qName);
}
@Override
public void characters(char ch[], int start, int length) {
System.out.println("内容: " + new String(ch, start, length));
}
@Override
public void endElement(String uri, String name, String qName) {
System.out.println("结束标签: " + qName);
}
}
生活中的类比:流水线工人
- 你站在流水线旁
- 产品(数据)一个接一个经过
- 你看到一个就处理一个
- 不需要记住之前的产品
- 优点:省空间,速度快
- 缺点:不能回头看前面的产品
SAX 的特点
| 优点 | 缺点 |
|---|---|
| ✅ 内存占用极低 | ❌ 只能顺序读取 |
| ✅ 处理速度快 | ❌ 不能随机访问 |
| ✅ 适合大文件 | ❌ 代码稍复杂 |
| ✅ 可以随时停止 | ❌ 需要自己维护状态 |
DOM (Document Object Model)
工作原理
XML 文件: → 解析器 → 内存中的树 → 你的代码操作
<root> ↓ ↓
<a>1</a> 全部读取 root
<b>2</b> 构建树 ├── a: "1"
</root> └── b: "2"
整棵树都在内存中!
代码示例
java
// DOM 解析后,整个文档树都在内存中
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("data.xml"));
// 可以随意访问任何节点
NodeList nodes = doc.getElementsByTagName("a");
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getTextContent());
}
// 可以修改节点
nodes.item(0).setTextContent("新值");
// 可以添加新节点
Element newElement = doc.createElement("c");
newElement.setTextContent("3");
doc.getDocumentElement().appendChild(newElement);
生活中的类比:图书馆管理员
- 把所有书(数据)都搬到阅览室
- 可以随时去翻任何一本书
- 可以在书上做笔记、修改
- 但需要很大的阅览室(内存)
- 优点:灵活方便
- 缺点:占空间,搬书耗时间
DOM 的特点
| 优点 | 缺点 |
|---|---|
| ✅ 可以随机访问任何节点 | ❌ 内存占用高 |
| ✅ 可以修改文档 | ❌ 加载整个文件慢 |
| ✅ 可以遍历多次 | ❌ 不适合大文件 |
| ✅ 代码简单直观 | ❌ 需要等待全部加载 |
直观对比
读取一个 10MB 的 XML 文件
SAX 方式
内存占用:约 20MB
处理时间:1 秒
工作方式:像看电视,边播边看
DOM 方式
内存占用:约 200MB+
处理时间:3 秒
工作方式:像下载电影,全下载完再看
具体示例对比
假设有这个 XML 文件:
xml
<students>
<student id="1">
<name>张三</name>
<age>20</age>
</student>
<student id="2">
<name>李四</name>
<age>21</age>
</student>
<student id="3">
<name>王五</name>
<age>22</age>
</student>
</students>
SAX 处理流程(事件驱动)
1. 开始标签 <students> → 触发 startElement()
2. 开始标签 <student> → 触发 startElement()
3. 开始标签 <name> → 触发 startElement()
4. 内容 "张三" → 触发 characters()
5. 结束标签 </name> → 触发 endElement()
6. 开始标签 <age> → 触发 startElement()
7. 内容 "20" → 触发 characters()
8. 结束标签 </age> → 触发 endElement()
9. 结束标签 </student> → 触发 endElement()
...(继续处理后面的学生)
DOM 处理流程(树状结构)
加载完成后,内存中是这样的树:
Document
└── students
├── student (id="1")
│ ├── name: "张三"
│ └── age: "20"
├── student (id="2")
│ ├── name: "李四"
│ └── age: "21"
└── student (id="3")
├── name: "王五"
└── age: "22"
然后你可以:
- 直接访问第3个学生:doc.getElementsByTagName("student").item(2)
- 修改年龄:student.getElementsByTagName("age").item(0).setTextContent("23")
- 删除某个学生
- 添加新学生
什么时候用哪种?
用 SAX 的场景 ✅
- 📄 大文件(> 10MB)
- 🚀 只需要读取一次(不需要回看)
- 💾 内存受限(嵌入式设备、高并发场景)
- 📊 数据导入(Excel、CSV 等批量处理)
- 🔍 提取特定信息(比如只找某个标签的内容)
用 DOM 的场景 ✅
- 📝 需要修改文档(增删改)
- 🔀 需要随机访问(跳到任意位置)
- 🔄 需要多次遍历
- 📦 小文件(< 1MB)
- 🛠️ 复杂查询(XPath、XSLT)
- 🎨 需要结构化操作(维护文档关系)
回到你的 Excel 解析问题
java
// SAX 方式(你看到的第一个代码)
XMLReader parser = XMLReaderFactory.createXMLReader(...);
parser.parse(sheetSource);
// 优点:处理 100MB 的 Excel 只需 50MB 内存
// DOM 方式(你看到的第二个代码)
Workbook workbook = WorkbookFactory.create(inputStream);
// 缺点:100MB 的 Excel 可能需要 2GB+ 内存
结论 :Excel 文件本质上是 XML(.xlsx 是 ZIP+XML),批量导入场景下,SAX 是更优的选择!
希望这个解释能帮你理解 SAX 和 DOM 的区别!