|
楼主 |
发表于 2015-12-21 13:18
|
显示全部楼层
接着之前说的.这里具体说说代码部分.
需求与之前的一样,从<数据源>中,查找第二列的内容中,开头是"普通",的行,复制到<结果输出>的工作表中
要在项目中引用
- 打开excel文档.
- 如果需要保存文档的修改内容,第二个参数要设置为true
- <P>using (SpreadsheetDocument doc = SpreadsheetDocument.Open(path, false))
- {</P>
- <P> //操作文档的逻辑</P>
- <P>}</P>
复制代码
获取指定工作表的对象:
- 正如之前所说,工作表的名字信息,是在Workbook对象下的Sheets才有.这里关键是需要获取我们需要的工作表的id
- Descendants方法,可以直达到某个指定类型的节点.这里的意思就是,获取Workbook下所有类型是Sheet的节点.然后使用linq的一个方法First,获取的一个符合条件的对象出来
- 然后通过WorkbookPart的方法GetPartById(),就可以获取我们要的工作表的WorksheetPart.
- GetPartById方法是OpenXmlElement对象的方法.很多对象都是他的子类.因此这些子类都有这个方法来获取下一层的part对象.但也是这个原因,这个方法的返回值是OpenXmlElement对象,因此需要显示转换才可以.
- WorkbookPart wbPart = doc.WorkbookPart;
- Sheet query_ws = wbPart.Workbook.Descendants<Sheet>().First(ws => ws.Name == "数据源");
- WorksheetPart wsPart = (WorksheetPart) wbPart.GetPartById(query_ws.Id);
复制代码
清除结果输出工作表的内容:
- 每个WorksheetPart下,都有SheetData对象来保存单元格数据内容
- 我发现在Worksheet对象,没有一个属性可以直接获取对应的SheetData对象(很多对象都是这样),这种情况一般使用Descendants方法就可以获取.例如这里可以通过.Descendants<Row>()就可以获取这个工作表下的所有行对象
- 这里偷懒,就直接用linq方法Skip跳过第一行标题行,然后使用方法Reverse来倒序输出遍历
- WorksheetPart outputSheet = (WorksheetPart) wbPart.GetPartById(wbPart.Workbook.Descendants<Sheet>().First(ws => ws.Name == "结果输出").Id);
- SheetData outputSheetData=outputSheet.Worksheet.Descendants<SheetData>().ElementAt(0);
- //删除行,记得按照常规,要从下往上清除行
- foreach (Row tmpRow in outputSheet.Worksheet.Descendants<Row>().Skip(1).Reverse())
- {
- tmpRow.Remove();
- }
复制代码
遍历处理每一行,每个单元格:
- 通过Row对象的ChildElements方法,可获取Cell对象.由于是继承OpenXmlElement对象的方法,所以要转换.
- 通过HasChildren判断是否有子元素,因为单元格有可能只有格式,没有值
- 通过DataType 属性,判断该单元格内容是什么类型.这个关系到我们获取到的值,到底是显示值本身,还是只是一个shareStringTable的索引
- 然后就是当内容是ShareString的时候,就去ShareTable找.通过ChildElements索引属性就可以找到.
- //从第二行开始遍历每一行
- for (int irow = 0; irow < rows.Length; irow++)
- {
- Row tmpRow=rows[irow];
- //获取第2列单元格
- Cell cell = (Cell) tmpRow.ChildElements[1];
- //单元格值是单元格的子元素,在有子元素情况下,才有值
- if (cell.HasChildren)
- {
- //这个单元格的值
- string value = cell.CellValue.Text;
- //这个单元格的内容,如果是一个excel文本类型,则这个value是一个sharestringtable表的索引.
- //如果是文本类型,才有Datatype
- if (cell.DataType != null && cell.DataType == CellValues.SharedString)
- {
- value = ssTable.ChildElements[int.Parse(value)].InnerText;
复制代码
把符合条件的行对象,复制到<结果输出>工作表中.
- 把符合条件的行对象,复制一个到新的行对象
- 记得要修改行索引和里面的Cell对象的地址
- 最后通过Append方法,把新的行对象添加到SheetData对象中
- //写入
- Row inputRow = (Row) tmpRow.Clone();
- inputRow.RowIndex = inputRowIndex;
- //修改cell的地址
- foreach (Cell tmpCell in inputRow)
- {
- tmpCell.CellReference.Value = tmpCell.CellReference.Value.Remove(1) + inputRowIndex.ToString();
- }
- outputSheetData.Append(inputRow);
复制代码
注意:
- 其实在打开文档之前,需要先判断该文件是否被占用.如果被占用,会抛出一个IO异常.
- 可以看出,使用openxml库,真的是很麻烦.如果自己不封装一下,真的难以长期使用.
|
|