打开从excel文件读取的xml
Posted
技术标签:
【中文标题】打开从excel文件读取的xml【英文标题】:open xml reading from excel file 【发布时间】:2014-05-30 21:17:39 【问题描述】:我想在我的项目中实现 openXml sdk 2.5。我在这个link做所有事情
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO.Packaging;
static void Main(string[] args)
String fileName = @"C:\OPENXML\BigData.xlsx";
// Comment one of the following lines to test the method separately.
ReadExcelFileDOM(fileName); // DOM
//ReadExcelFileSAX(fileName); // SAX
// The DOM approach.
// Note that the code below works only for cells that contain numeric values.
//
static void ReadExcelFileDOM(string fileName)
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
string text;
int rowCount= sheetData.Elements<Row>().Count();
foreach (Row r in sheetData.Elements<Row>())
foreach (Cell c in r.Elements<Cell>())
text = c.CellValue.Text;
Console.Write(text + " ");
Console.WriteLine();
Console.ReadKey();
但我没有得到任何行。它没有进入循环。注意:我的电脑也设置了openXml sdk 2.5
我发现下面的代码适用于数值。对于字符串值它写 0 1 2 ...
private static void Main(string[] args)
var filePath = @"C:/OPENXML/BigData.xlsx";
using (var document = SpreadsheetDocument.Open(filePath, false))
var workbookPart = document.WorkbookPart;
var workbook = workbookPart.Workbook;
var sheets = workbook.Descendants<Sheet>();
foreach (var sheet in sheets)
var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
var sharedStringPart = workbookPart.SharedStringTablePart;
//var values = sharedStringPart.SharedStringTable.Elements<SharedStringItem>().ToArray();
string text;
var rows = worksheetPart.Worksheet.Descendants<Row>();
foreach (var row in rows)
Console.WriteLine();
int count = row.Elements<Cell>().Count();
foreach (Cell c in row.Elements<Cell>())
text = c.CellValue.InnerText;
Console.Write(text + " ");
Console.ReadLine();
【问题讨论】:
之所以写 0, 1, 2... 是因为 Excel 使用的共享字符串表。有关访问实际文本的基本方法,请参阅下面的答案。 【参考方案1】:您的方法对我来说似乎没问题 - 因为它确实“进入了循环”。 不过,您也可以尝试以下方法:
void Main()
string fileName = @"c:\path\to\my\file.xlsx";
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (SpreadsheetDocument doc = SpreadsheetDocument.Open(fs, false))
WorkbookPart workbookPart = doc.WorkbookPart;
SharedStringTablePart sstpart = workbookPart.GetPartsOfType<SharedStringTablePart>().First();
SharedStringTable sst = sstpart.SharedStringTable;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
Worksheet sheet = worksheetPart.Worksheet;
var cells = sheet.Descendants<Cell>();
var rows = sheet.Descendants<Row>();
Console.WriteLine("Row count = 0", rows.LongCount());
Console.WriteLine("Cell count = 0", cells.LongCount());
// One way: go through each cell in the sheet
foreach (Cell cell in cells)
if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
int ssid = int.Parse(cell.CellValue.Text);
string str = sst.ChildElements[ssid].InnerText;
Console.WriteLine("Shared string 0: 1", ssid, str);
else if (cell.CellValue != null)
Console.WriteLine("Cell contents: 0", cell.CellValue.Text);
// Or... via each row
foreach (Row row in rows)
foreach (Cell c in row.Elements<Cell>())
if ((c.DataType != null) && (c.DataType == CellValues.SharedString))
int ssid = int.Parse(c.CellValue.Text);
string str = sst.ChildElements[ssid].InnerText;
Console.WriteLine("Shared string 0: 1", ssid, str);
else if (c.CellValue != null)
Console.WriteLine("Cell contents: 0", c.CellValue.Text);
我使用文件流方法打开工作簿,因为这允许您以共享访问权限打开它 - 这样您就可以同时在 Excel 中打开工作簿。如果工作簿在其他地方打开,则 Spreadsheet.Open(... 方法将不起作用。
也许这就是你的代码不起作用的原因。
还要注意,在适当的地方使用 SharedStringTable 来获取单元格文本。
编辑 2018-07-11:
由于这篇文章仍在获得投票,我还应该指出,在许多情况下,使用ClosedXML 来操作/阅读/编辑您的工作簿可能要容易得多。文档示例非常用户友好,并且根据我有限的经验,编码更直接。请注意,它(尚未)实现所有 Excel 函数(例如 INDEX 和 MATCH),这可能是也可能不是问题。 [无论如何我都不想尝试在 OpenXML 中处理 INDEX 和 MATCH。]
【讨论】:
我认为你的代码绝对有效,但是当我将上面的代码复制到我的项目时它不起作用,我不知道为什么它不起作用。 那么你需要调试它或者提供比“它不起作用”更多的信息。例如,它在什么时候失败,是否产生异常,行数和单元格数是多少。此外,例如,一些 Assert 语句 - 例如 Assert(workbookPart != null);断言(工作表部分!= null);断言(表!= null);等等等等。 它创建关于 workbookPart 工作表部分的对象。但是当我想检查 ResultViews 我看到它 Empty = "Enumeration yielded no results" 我更改了 openxml 版本 v2.0.50727 和 Windowsbase v4.0.30319。版本有问题吗? 我在答案中提到了它 - 大多数在线文档都建议使用 SpreadsheetDocument.Open(... 方法,这很好用 - 但只有在工作簿没有在其他地方打开时才有效。为了什么我正在做我需要能够在 Excel 中打开工作簿并仍然使用 OpenXML 读取它。文件流方法允许我指定文件打开和共享模式。显然,如果尝试写入工作簿,但我想通过 OpenXML 进行只读访问。我怀疑它会更快,因为我认为 OpenXML 无论如何都会使用文件流。【参考方案2】:我和 OP 有同样的问题,上面的答案对我不起作用。
我认为这是问题所在:当您在 Excel 中(不是以编程方式)创建文档时,默认情况下您有 3 张工作表,而具有 Sheet1 行数据的 WorksheetParts 是最后一个 WorksheetParts 元素,而不是第一个。
我通过在 Visual Studio 中监视 document.WorkbookPart.WorksheetParts、展开结果、然后查看所有子元素直到找到 HasChildren = true 的 SheetData 对象来解决这个问题。
试试这个:
// open the document read-only
SpreadSheetDocument document = SpreadsheetDocument.Open(filePath, false);
SharedStringTable sharedStringTable = document.WorkbookPart.SharedStringTablePart.SharedStringTable;
string cellValue = null;
foreach (WorksheetPart worksheetPart in document.WorkbookPart.WorksheetParts)
foreach (SheetData sheetData in worksheetPart.Worksheet.Elements<SheetData>())
if (sheetData.HasChildren)
foreach (Row row in sheetData.Elements<Row>())
foreach (Cell cell in row.Elements<Cell>())
cellValue = cell.InnerText;
if (cell.DataType == CellValues.SharedString)
Console.WriteLine("cell val: " + sharedStringTable.ElementAt(Int32.Parse(cellValue)).InnerText);
else
Console.WriteLine("cell val: " + cellValue);
document.Close();
【讨论】:
谢谢,很好的例子!【参考方案3】:阅读大型 Excel: openxml 有 DOM 和 SAX 两种读取 excel 的方法。 DOM 消耗更多的 RAM 资源,因为它将整个 xml 内容(Excel 文件)加载到内存中,但它的强类型化方法。 另一方面,SAX 是事件库解析。 more here
因此,如果您面对大型 excel 文件,最好使用 SAX。
以下代码示例使用 SAX 方法,同时处理 excel 文件读取中的两个重要场景。
-
open xml 会跳过空单元格,因此您的数据集面临位移和错误索引。
您还需要跳过空行。
此函数返回当时单元格的确切实际索引并处理第一种情况。 from here
private static int CellReferenceToIndex(Cell cell)
int index = 0;
string reference = cell.CellReference.ToString().ToUpper();
foreach (char ch in reference)
if (Char.IsLetter(ch))
int value = (int)ch - (int)'A';
index = (index == 0) ? value : ((index + 1) * 26) + value;
else
return index;
return index;
读取excel sax方法的代码。
//i want to import excel to data table
dt = new DataTable();
using (SpreadsheetDocument document = SpreadsheetDocument.Open(path, false))
WorkbookPart workbookPart = document.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);
//row counter
int rcnt = 0;
while (reader.Read())
//find xml row element type
//to understand the element type you can change your excel file eg : test.xlsx to test.zip
//and inside that you may observe the elements in xl/worksheets/sheet.xml
//that helps to understand openxml better
if (reader.ElementType == typeof(Row))
//create data table row type to be populated by cells of this row
DataRow tempRow = dt.NewRow();
//***** HANDLE THE SECOND SENARIO*****
//if row has attribute means it is not a empty row
if (reader.HasAttributes)
//read the child of row element which is cells
//here first element
reader.ReadFirstChild();
do
//find xml cell element type
if (reader.ElementType == typeof(Cell))
Cell c = (Cell)reader.LoadCurrentElement();
string cellValue;
int actualCellIndex = CellReferenceToIndex(c);
if (c.DataType != null && c.DataType == CellValues.SharedString)
SharedStringItem ssi = workbookPart.SharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAt(int.Parse(c.CellValue.InnerText));
cellValue = ssi.Text.Text;
else
cellValue = c.CellValue.InnerText;
//if row index is 0 its header so columns headers are added & also can do some headers check incase
if (rcnt == 0)
dt.Columns.Add(cellValue);
else
// instead of tempRow[c.CellReference] = cellValue;
tempRow[actualCellIndex] = cellValue;
while (reader.ReadNextSibling());
//if its not the header row so append rowdata to the datatable
if (rcnt != 0)
dt.Rows.Add(tempRow);
rcnt++;
【讨论】:
以上是关于打开从excel文件读取的xml的主要内容,如果未能解决你的问题,请参考以下文章