【问题标题】:How to read faster in OpenXML format如何以 OpenXML 格式更快地阅读
【发布时间】:2012-09-29 16:32:45
【问题描述】:

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

下面是我的代码:

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; 
} 

是否有一种有效的方法可以在某处更改此代码,以便读取过程更快一些。我如何将其更改为代码以更快地阅读。谢谢。

【问题讨论】:

  • 我把你的代码执行到一个包含大约 . 3,500 行。花了 4 秒 - 你的 Excel 表格中有什么数据?
  • {$REGIONAL PREFIX},CITY,DATE,FUNCTION,LOCATION,KEY MAP,ORDER NUMBER,STATE,STREET NUMBER,STREET NAME,UNIT NUMBER ,SEND TO GIS,FILENAME - 这些都是标题.一行数值如下:61||32601,E,CUSHING,6/14/1966,X-SVCS,339,AR,111-421,W WALNUT-N CLEVELAND,X,||111-421 W WALNUT -N CLEVELAND.pdf
  • 您可能想尝试使用 EPPLUS 来加载 xlsx 文件。在我看来,它要快得多。
  • 用您给定的详细信息进行了尝试 - 大约 10 秒。如果您删除您的console.writeline stmts,它会将时间减少到1.26秒!!!
  • @ChristianSauer,非常感谢。 EPPLUS 按预期工作正常。谢谢。

标签: c# .net excel c#-4.0 openxml


【解决方案1】:

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

在将我的.xls file 编辑到您的给定详细信息(列:区域前缀、城市、日期、功能...)并添加大约 3,600 行您的代码之后最多大约 10 秒

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

即使在 SO: Console.WriteLine slow 上,您也可以找到 console.WriteLine 如此缓慢的一些原因。在这个问题中有一个答案指向OutputDebugString...

【讨论】:

    【解决方案2】:

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

    1. 向 DataTable 添加大量行时,使用 BeginLoadData 和 EndLoadData
    2. 您需要缓存 SharedStringTable
    3. 您应该使用 OpenXmlReader(SAX 方法)。内存消耗将减少。

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

    读取数据表示例:

    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();
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-06-07
      • 2022-08-06
      • 1970-01-01
      • 2023-03-27
      • 2021-04-02
      • 2018-11-15
      • 1970-01-01
      • 2019-12-16
      • 2012-05-04
      相关资源
      最近更新 更多