文章摘要
本文介绍了操作系统和应用程序在Excel文件处理中的分工。操作系统仅负责文件存储管理和类型识别,不解析内容;而应用程序则负责解析Excel文件的具体格式。对于.xlsx文件,应用程序会先解压zip包,再解析其中的XML文件(如workbook.xml)重建表格数据。文章以C#的ExcelDataReader库为例,展示了从文件打开到数据读取的具体流程。总结指出:操作系统管文件存取,应用程序管解析内容,开发者通过调用库API实现Excel操作。
打开文件
应用程序通过操作系统的文件 API(如 fopen、File.Open)获取文件流。
解压缩
.xlsx 文件其实是一个 zip 包,应用程序先解压,获得内部的 XML 文件。
读取 XML
解析 xl/workbook.xml
(工作簿信息)、xl/worksheets/sheet1.xml
(表格数据)、xl/sharedStrings.xml
(字符串池)等。
解析数据结构
通过 XML 解析器(如 System.Xml、libxml2)读取行、列、单元格内容,重建为内存中的表格对象。
数据访问
应用程序根据需要访问、修改、保存数据。
using (var stream = File.Open("test.xlsx", FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateOpenXmlReader(stream))
{
var result = reader.AsDataSet();
// result.Tables[0] 就是第一个sheet的数据
}
}
File.Open
:操作系统提供的文件流。ExcelReaderFactory.CreateOpenXmlReader
:库内部解压并解析 XML。AsDataSet
:将表格数据转为 DataSet 结构,便于程序访问。下面详细讲解 ExcelReaderFactory.CreateReader
的实现原理和流程。
ExcelReaderFactory.CreateReader
是什么?ExcelReaderFactory.CreateReader(Stream stream)
是 ExcelDataReader 库的一个静态工厂方法。
它的作用是:根据输入的 Excel 文件流(.xls 或 .xlsx),自动识别格式,返回一个合适的 IExcelDataReader 实例,用于后续读取表格内容。
.xls
(BIFF,二进制格式,Excel 97-2003).xlsx
(OpenXml,基于 XML 的压缩包,Excel 2007+)CreateReader
会读取文件流的前几个字节(magic number),判断是 xls 还是 xlsx。
(以 v3.6.0 为例,实际代码有简化)
public static IExcelDataReader CreateReader(Stream fileStream)
{
if (fileStream == null)
throw new ArgumentNullException(nameof(fileStream));
// 读取前4个字节判断文件类型
byte[] header = new byte[4];
int bytesRead = fileStream.Read(header, 0, 4);
fileStream.Position = 0; // 还原流位置
// XLSX: PK\x03\x04
if (header[0] == 0x50 && header[1] == 0x4B)
{
return new ExcelOpenXmlReader(fileStream); // 处理 .xlsx
}
// XLS: 0xD0 0xCF 0x11 0xE0
else if (header[0] == 0xD0 && header[1] == 0xCF)
{
return new ExcelBinaryReader(fileStream); // 处理 .xls
}
else
{
throw new ArgumentException("Unknown Excel format");
}
}
.xlsx
返回 ExcelOpenXmlReader
(实现了 IExcelDataReader).xls
返回 ExcelBinaryReader
(实现了 IExcelDataReader)这两个类都实现了统一的接口 IExcelDataReader
,你可以用同样的方式读取数据。
ExcelReaderFactory.CreateReader(stream)
IExcelDataReader
实例.AsDataSet()
或 .Read()
读取数据示例:
using (var stream = File.Open("test.xlsx", FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
var result = reader.AsDataSet();
// 读取数据
}
}
CreateOpenXmlReader
(只读 xlsx)或 CreateBinaryReader
(只读 xls)强制指定格式。CreateReader
是最常用的自动识别入口,推荐优先使用。ExcelReaderFactory.CreateReader
通过读取文件头自动识别 Excel 格式。IExcelDataReader
实例,便于后续无差别读取。下面将深入讲解 ExcelDataReader 库中 ExcelOpenXmlReader
和 ExcelBinaryReader
的底层实现原理和主要流程。
(注:源码可参考 ExcelDataReader GitHub)
IExcelDataReader
:统一的接口,定义了读取 Excel 的方法(如 Read()
, AsDataSet()
等)。ExcelOpenXmlReader
:实现了对 .xlsx
(OpenXML)格式的解析。ExcelBinaryReader
:实现了对 .xls
(BIFF)格式的解析。ExcelReaderFactory.CreateReader
会根据文件头返回这两个类的实例。
.xlsx
文件本质是一个 ZIP 压缩包,里面包含多个 XML 文件。xl/workbook.xml
:工作簿信息xl/worksheets/sheet1.xml
:表格数据xl/sharedStrings.xml
:字符串池xl/styles.xml
:样式信息解压 ZIP 包
System.IO.Packaging
或 System.IO.Compression
读取 ZIP 结构。解析 sharedStrings.xml
解析 workbook.xml
解析 sheetN.xml
|
和
(cell)节点。实现 IExcelDataReader 接口
Read()
:移动到下一行,解析当前行的所有单元格。GetValue(int i)
:返回当前行第 i 列的值。AsDataSet()
:将所有 sheet 读入 DataSet。// 1. 解压并读取 sharedStrings
var sharedStrings = ReadSharedStrings("xl/sharedStrings.xml");
// 2. 读取 workbook.xml,获取 sheet 列表
var sheets = ReadSheets("xl/workbook.xml");
// 3. 读取每个 sheetN.xml
foreach (var sheet in sheets)
{
foreach (var row in ReadRows(sheet.XmlPath))
{
foreach (var cell in row.Cells)
{
if (cell.Type == "s") // shared string
value = sharedStrings[cell.Value];
else
value = ParseCellValue(cell);
}
}
}
.xls
是微软的 BIFF(Binary Interchange File Format)格式,二进制存储。读取文件头
逐条解析 Record
处理单元格数据
实现 IExcelDataReader 接口
Read()
:移动到下一行,解析当前行的所有单元格。GetValue(int i)
:返回当前行第 i 列的值。AsDataSet()
:将所有 sheet 读入 DataSet。while (stream.Position < stream.Length)
{
var recordType = ReadUInt16();
var recordLength = ReadUInt16();
var recordData = ReadBytes(recordLength);
switch (recordType)
{
case BOF: // 新的 sheet
StartNewSheet();
break;
case ROW:
StartNewRow();
break;
case NUMBER:
ParseNumberCell(recordData);
break;
case LABELSST:
ParseStringCell(recordData, sstTable);
break;
// ... 其他类型
}
}
特点 | ExcelOpenXmlReader (.xlsx) | ExcelBinaryReader (.xls) |
---|---|---|
文件结构 | ZIP+XML | 二进制(BIFF) |
解析方式 | 解压+XML流式解析 | 二进制流式解析 |
字符串存储 | sharedStrings.xml | SST(Shared String Table) |
兼容性 | Excel 2007+ | Excel 97-2003 |
代码复杂度 | XML解析 | 二进制协议解析 |
ExcelOpenXmlReader
通过解压 ZIP 包、解析 XML 文件流式读取 .xlsx。ExcelBinaryReader
通过解析二进制 BIFF 记录流式读取 .xls。IExcelDataReader
,对上层调用者透明。