还有什么不能做?——细谈在C#中读写Excel系列文章之二

简介:

在前一篇文章中我谈到了Excel 2007以后版本的文件其实就是一个zip压缩包,里面包含了该Excel文件的所有内容和使用的资源,大部分文件都是以XML的形式存放的。再来回顾一下解压之后的文件夹结构,

  其中比较重要的是xl文件夹,里面包含了一些很有用的信息。sharedStrings.xml文件存放了Excel中所有的字符串数据。

  worksheets文件夹中存放了所有的工作表信息,如果你的Excel文件中包含多个工作表,则这里你会发现有多个与工作表对应的xml文件。

  来看一下sharedStrings.xml和工作表xml文件之间的关系。首先找到对应的工作表xml文件,如上图所示文件夹中的sheet3.xml,找到该xml文件的sheetData节点,下面包含有row节点,用来表示该工作表中的每一行,属性r代表行号。子节点c表示了该行中的每一个单元格,属性r表示单元格在Excel工作表的位置,如“A1”。注意属性t="s",它表示该单元格的内容是存储在sharedStrings.xml文件中的,其子节点v的值用来说明该值在sharedStrings.xml文档中的位置。

  sharedStrings.xml文件中包含有很多si节点,其中存放的就是Excel文件里各个工作表中的字符串数据。上图中v节点的值代表的就是sharedStrings.xml文件中si节点的序号,该序号从1开始计数。通过这个关系,我们就可以在sharedStrings.xml文件中找到我们想要的数据了。

  弄清楚这个关系之后,我们再来看看如何通过代码取到我们想要的数据。从.NET Framework 3.0之后提供了一个新的类库可以帮助我们直接读取Excel包里面的文件。如果你的.NET工程是普通的Windows应用程序,那可以直接在工程中引用WindowsBase.dll类库。如果你的工程是非Windows应用程序,如Silverlight,则无法引用该类库,在后续的文章中我还会再介绍如何在Silverlight工程中读取Excel包里的文件。

  还是直接上代码吧,写太多文字大家看得也累!

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Windows.Forms;
  9 using System.IO;
 10 using System.IO.Packaging;
 11 using System.Xml;
 12 using System.Xml.Linq;
 13 
 14 namespace ExcelPackageOperator
 15 {
 16     public partial class Form1 : Form
 17     {
 18         public Form1()
 19         {
 20             InitializeComponent();
 21 
 22             List<Cell> parsedCells = new List<Cell>();
 23             string fileName = @"C:\Users\jaxu\Desktop\Example.xlsx"; 
 24 
 25             using(Package xlsxPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read))
 26             {
 27                 // load sharedStrings.xml document from Excel package
 28                 PackagePartCollection allParts = xlsxPackage.GetParts();
 29                 PackagePart sharedStringsPart = (from part in allParts
 30                                                  where part.Uri.OriginalString.Equals("/xl/sharedStrings.xml")
 31                                                  select part).Single();
 32 
 33                 // load to XElement from sharedStrings.xml document
 34                 XElement sharedStringsElement = XElement.Load(XmlReader.Create(sharedStringsPart.GetStream())); 
 35 
 36                 // read all fixed text to a dictionary from sharedStrings document
 37                 Dictionary<int, string> sharedStrings = new Dictionary<int, string>(); 
 38                 ParseSharedStrings(sharedStringsElement, sharedStrings); 
 39                 
 40                 // get worksheet 1
 41                 XElement worksheetElement = GetWorksheet(3, allParts); 
 42                 
 43                 IEnumerable<XElement> cells = from c in worksheetElement.Descendants(ExcelNamespaces.excelNamespace + "c") 
 44                                               select c; 
 45                 
 46                 foreach (XElement cell in cells) 
 47                 {
 48                     if (cell.HasElements && cell.Attribute("t") != null && cell.Attribute("t").Value.Equals("s"))
 49                     {
 50                         string cellPosition = cell.Attribute("r").Value;
 51                         int index = IndexOfNumber(cellPosition);
 52                         string column = cellPosition.Substring(0, index);
 53                         int row = Convert.ToInt32(cellPosition.Substring(index, cellPosition.Length - index));
 54                         int valueIndex = Convert.ToInt32(cell.Descendants(ExcelNamespaces.excelNamespace + "v").Single().Value);
 55 
 56                         parsedCells.Add(new Cell(column, row, sharedStrings[valueIndex]));
 57                     }
 58                 } 
 59             }
 60             
 61             //From here is additional code not covered in the posts, just to show it works
 62             foreach (Cell cell in parsedCells)            
 63             {
 64                 this.textBox1.Text += cell.ToString() + "\r\n";
 65                 //Console.WriteLine(cell);            
 66             }
 67         }
 68 
 69         private void ParseSharedStrings(XElement SharedStringsElement, Dictionary<int, string> sharedStrings) 
 70         { 
 71             IEnumerable<XElement> sharedStringsElements = from s in SharedStringsElement.Descendants(ExcelNamespaces.excelNamespace + "si") 
 72                                                           select s; 
 73             
 74             int Counter = 0; 
 75             foreach (XElement sharedString in sharedStringsElements) 
 76             { 
 77                 sharedStrings.Add(Counter, sharedString.Value); Counter++; 
 78             } 
 79         }         
 80         
 81         private XElement GetWorksheet(int worksheetID, PackagePartCollection allParts) 
 82         { 
 83             PackagePart worksheetPart = (from part in allParts 
 84                                          where part.Uri.OriginalString.Equals(String.Format("/xl/worksheets/sheet{0}.xml", worksheetID)) 
 85                                          select part).Single(); 
 86             return XElement.Load(XmlReader.Create(worksheetPart.GetStream())); 
 87         }
 88         
 89         private int IndexOfNumber(string value) 
 90         { 
 91             for (int counter = 0; counter < value.Length; counter++) 
 92             { 
 93                 if (char.IsNumber(value[counter])) 
 94                 { 
 95                     return counter; 
 96                 }
 97             }
 98             
 99             return 0; 
100         }
101     }
102 
103     internal static class ExcelNamespaces 
104     { 
105         internal static XNamespace excelNamespace = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); 
106         internal static XNamespace excelRelationshipsNamepace = XNamespace.Get("http://schemas.openxmlformats.org/officeDocument/2006/relationships");    
107     }
108 
109     public class Cell 
110     {
111         public Cell(string column, int row, string data) 
112         {
113             this.Column = column; 
114             this.Row = row; 
115             this.Data = data; 
116         }
117         
118         public override string ToString() 
119         {
120             return string.Format("{0}:{1} - {2}", Row, Column, Data); 
121         }
122         
123         public string Column 
124         {
125             get; set; 
126         }
127         
128         public int Row 
129         {
130             get; set; 
131         }
132         
133         public string Data 
134         {
135             get; set; 
136         }
137     }
138 }
复制代码

  1. 使用Package类直接打开Excel文件,该类属于System.IO.Packing命名空间,必须确保工程中引用了WindowsBase.dll类库。

  2. 通过GetParts()方法读取包里面的内容。注意LINQ语句取到的是sharedStrings.xml文件。

  3. 将读取到的sharedStrings.xml文件加载到XElement对象中,然后通过PaseSharedStrings这个自定义函数读取里面所有的si子节点并存放到一个字典里。

  4. 自定义函数GetWorksheet用来将指定的工作表xml从包中读取出来加载到XElement对象中。

  5. 程序接下来的部分就是遍历工作表xml中所有的节点c,并从属性r中找出单元格的行编号和列编号。然后将准备好的数据存储到parsedCells集合中,该集合的类型是一个自定义的Cell对象,该对象包含三个属性(行编号、列编号和数据内容)和一个改写的ToString()方法,该方法用来定义数据输出格式。

  注意数据是从字典中取出来的,字典的Key值就是节点r的子节点v的值!还记得文章一开始我们介绍的sharedStrings.xml文件和工作表xml文件之间的关系吗?这里就是借用的这个关系。另外在遍历工作表xml中的节点c时是过滤掉了那些不包含有属性t="s"的节点的,因为这些节点直接存储了数据,而不需要再从sharedStrings.xml去取。

  6. 输出parsedCells集合中的数据。

  好了,程序的结构就是这样。代码可能稍稍有点长,只看主要的部分,其实还是很简单的:借用WindowsBase类库中的System.IO.Packing.Package对象来读取Excel包里的内容,然后找出sharedStrings.xml文件中的所有字符串数据并存放到一个字典中,遍历你想要操作的工作表xml文件,按单元格的位置从字典中找出对应的数据显示出来。

  不过还是有一个遗憾。在使用.NET现有的功能时总会遇到一些问题!该对象并没有提供对Excel文件的修改和保存功能,至少我没有发现。也就是说它可以允许我们在不调用COM组件的情况下非常方便地读取Excel文件中的数据,包括字符串和数字。另外还有一个问题就是该类库不能在Silverlight工程中引用,如果你刚好想在Silverlight工程中使用该功能那么只能想其它的方法了。不过有一个问题值得思考,那就是既然Excel是一个zip压缩包,而且里面大部分的数据都是以xml文件的格式存储的,我们何不自己写代码解压然后读取或者修改xml文件呢?甚至我们还可以把修改之后的文件重新打包还原成一个新的Excel文件,这样的话我们就可以实现Excel文件内容的修改,而且也可以在Silverlight工程中使用了。

  我会在接下来的文章中介绍如何把Excel文件作为zip压缩包来进行操作,包括读取和修改。


本文转自Jaxu博客园博客,原文链接:http://www.cnblogs.com/jaxu/archive/2012/05/10/2493627.html,如需转载请自行联系原作者

相关文章
|
15天前
|
物联网 数据处理 C#
C#实现上位机开发,串口通信,读写串口数据并处理16进制数据
C#实现上位机开发,串口通信,读写串口数据并处理16进制数据。在自动化、物联网以及工业控制行业中,上位机开发是一项重要的技能。本教程主要介绍使用C#进行上位机开发,重点在于串口通信和数据处理。
197 82
|
4月前
|
Python
python读写操作excel日志
主要是读写操作,创建表格
83 2
|
4月前
|
索引 Python
Excel学习笔记(一):python读写excel,并完成计算平均成绩、成绩等级划分、每个同学分数大于70的次数、找最优成绩
这篇文章是关于如何使用Python读取Excel文件中的学生成绩数据,并进行计算平均成绩、成绩等级划分、统计分数大于70的次数以及找出最优成绩等操作的教程。
141 0
|
5月前
|
SQL C# 数据库
EPPlus库的安装和使用 C# 中 Excel的导入和导出
本文介绍了如何使用EPPlus库在C#中实现Excel的导入和导出功能。首先,通过NuGet包管理器安装EPPlus库,然后提供了将DataGridView数据导出到Excel的步骤和代码示例,包括将DataGridView转换为DataTable和使用EPPlus将DataTable导出为Excel文件。接着,介绍了如何将Excel数据导入到数据库中,包括读取Excel文件、解析数据、执行SQL插入操作。
EPPlus库的安装和使用 C# 中 Excel的导入和导出
对excel读写的三个模块,xlsxwriter最牛,xlwt , xlrd,openpyxl
对excel读写的三个模块,xlsxwriter最牛,xlwt , xlrd,openpyxl
|
6月前
|
SQL JSON 关系型数据库
n种方式教你用python读写excel等数据文件
n种方式教你用python读写excel等数据文件
98 1
|
6月前
|
easyexcel Java 关系型数据库
阿里巴巴-EasyExcel 基于Java的简单、省内存的读写Excel
该文章主要介绍了在Java应用中如何使用EasyExcel技术完成对Excel文件的导入和导出操作,包括环境搭建、基本概念、快速入门、进阶操作和综合应用等内容,并提供了相关代码示例和注意事项。
 阿里巴巴-EasyExcel 基于Java的简单、省内存的读写Excel
|
6月前
|
开发框架 算法 .NET
C#使用MiniExcel导入导出数据到Excel/CSV文件
C#使用MiniExcel导入导出数据到Excel/CSV文件
157 0
|
2月前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
116 12
|
3月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
117 4

热门文章

最新文章