如何更快地读取OpenXML格式

当我使用OLEDB时,从Excel工作表中读取3200行只需要2 – 3秒。 现在我改成了OpenXML格式,现在需要1分多钟才能读取Excel工作表中的3200行。

以下是我的代码:

public static DataTable ReadExcelFileDOM(string filename) { DataTable table; using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open(filename, true)) { WorkbookPart workbookPart = myDoc.WorkbookPart; Sheet worksheet = workbookPart.Workbook.Descendants<Sheet>().First(); WorksheetPart worksheetPart = (WorksheetPart)(workbookPart.GetPartById(worksheet.Id)); SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First(); List<List<string>> totalRows = new List<List<string>>(); int maxCol = 0; foreach (Row r in sheetData.Elements<Row>()) { // Add the empty row. string value = null; while (totalRows.Count < r.RowIndex - 1) { List<string> emptyRowValues = new List<string>(); for (int i = 0; i < maxCol; i++) { emptyRowValues.Add(""); } totalRows.Add(emptyRowValues); } List<string> tempRowValues = new List<string>(); foreach (Cell c in r.Elements<Cell>()) { #region get the cell value of c. if (c != null) { value = c.InnerText; // If the cell represents a numeric value, you are done. // For dates, this code returns the serialized value that // represents the date. The code handles strings and Booleans // individually. For shared strings, the code looks up the // corresponding value in the shared string table. For Booleans, // the code converts the value into the words TRUE or FALSE. if (c.DataType != null) { switch (c.DataType.Value) { case CellValues.SharedString: // For shared strings, look up the value in the shared // strings table. var stringTable = workbookPart. GetPartsOfType<SharedStringTablePart>().FirstOrDefault(); // If the shared string table is missing, something is // wrong. Return the index that you found in the cell. // Otherwise, look up the correct text in the table. if (stringTable != null) { value = stringTable.SharedStringTable. ElementAt(int.Parse(value)).InnerText; } break; case CellValues.Boolean: switch (value) { case "0": value = "FALSE"; break; default: value = "TRUE"; break; } break; } } Console.Write(value + " "); } #endregion // Add the cell to the row list. int i = Convert.ToInt32(c.CellReference.ToString().ToCharArray().First() - 'A'); // Add the blank cell in the row. while (tempRowValues.Count < i) { tempRowValues.Add(""); } tempRowValues.Add(value); } // add the row to the totalRows. maxCol = processList(tempRowValues, totalRows, maxCol); Console.WriteLine(); } table = ConvertListListStringToDataTable(totalRows, maxCol); } return table; } /// <summary> /// Add each row to the totalRows. /// </summary> /// <param name="tempRows"></param> /// <param name="totalRows"></param> /// <param name="MaxCol">the max column number in rows of the totalRows</param> /// <returns></returns> private static int processList(List<string> tempRows, List<List<string>> totalRows, int MaxCol) { if (tempRows.Count > MaxCol) { MaxCol = tempRows.Count; } totalRows.Add(tempRows); return MaxCol; } private static DataTable ConvertListListStringToDataTable(List<List<string>> totalRows, int maxCol) { DataTable table = new DataTable(); for (int i = 0; i < maxCol; i++) { table.Columns.Add(); } foreach (List<string> row in totalRows) { while (row.Count < maxCol) { row.Add(""); } table.Rows.Add(row.ToArray()); } return table; } 

有没有一个有效的方法来改变这个代码的地方,使读取过程可以快一点。 我怎样才能改变这个代码来更快的阅读。 谢谢。

我试过你的代码,并注意到在一个非常简单的例子中 ,我花了大约4秒来完成。

编辑我的.xls file到你给定的细节 (列:区域前缀,城市,date,function,…),并增加约3600行,你的代码约10秒

我想你应该删除任何Console.WriteLine语句,因为这些语句减慢处理你的xls file 。 删除所有这些后,我的StopWatch显示1.26秒的相同行数。

你可以find一些原因为什么console.WriteLine是如此缓慢,即使在SO:Console.WriteLine慢 。 在这个问题有一个答案指向OutputDebugString

我在代码中发现了一些缺点。

  1. 当添加到DataTable时,使用BeginLoadData和EndLoadData的行数很多
  2. 你需要cachingSharedStringTable
  3. 你应该使用OpenXmlReader(SAX方法)。 内存消耗会减less。

您可以尝试我的ExcelDataReader没有这些缺点。 看到这里https://github.com/gSerP1983/OpenXml.Excel.Data

阅读DataTable示例:

 class Program { static void Main(string[] args) { var dt = new DataTable(); using (var reader = new ExcelDataReader(@"data.xlsx")) { dt.Load(reader); } Console.WriteLine("done: " + dt.Rows.Count); Console.ReadKey(); } }