C#讀取Excel的三種方式以及比較分析

來源:互聯網
上載者:User
(1)OleDB方式

優點:將Excel直接當做資料來源處理,通過SQL直接讀取內容,讀取速度較快。

缺點:讀取資料方式不夠靈活,無法直接讀取某一個儲存格,只有將整個Sheet頁讀取出來後(結果為Datatable)再在Datatable中根據行列數來擷取指定的值。

當Excel資料量很大時。會非常佔用記憶體,當記憶體不夠時會拋出記憶體溢出的異常。

讀取代碼如下:

 public DataTable GetExcelTableByOleDB(string strExcelPath, string tableName){  try  {    DataTable dtExcel = new DataTable();    //資料表    DataSet ds = new DataSet();    //擷取副檔名    string strExtension = System.IO.Path.GetExtension(strExcelPath);    string strFileName = System.IO.Path.GetFileName(strExcelPath);    //Excel的串連    OleDbConnection objConn = null;    switch (strExtension)    {      case ".xls":        objConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strExcelPath + ";" + "Extended Properties=\"Excel 8.0;HDR=NO;IMEX=1;\"");        break;      case ".xlsx":        objConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + strExcelPath + ";" + "Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1;\"");        break;      default:        objConn = null;        break;    }    if (objConn == null)    {      return null;    }    objConn.Open();    //擷取Excel中所有Sheet表的資訊    //System.Data.DataTable schemaTable = objConn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);    //擷取Excel的第一個Sheet表名    //string tableName = schemaTable.Rows[0][2].ToString().Trim();    string strSql = "select * from [" + tableName + "]";    //擷取Excel指定Sheet表中的資訊    OleDbCommand objCmd = new OleDbCommand(strSql, objConn);    OleDbDataAdapter myData = new OleDbDataAdapter(strSql, objConn);    myData.Fill(ds, tableName);//填充資料    objConn.Close();    //dtExcel即為excel檔案中指定表中儲存的資訊    dtExcel = ds.Tables[tableName];    return dtExcel;  }  catch  {    return null;  }}

下面說明一下連接字串

HDR=Yes,這代表第一行是標題,不做為資料使用(但是我在實際使用中,如果第一行存在複雜數值,那麼讀取得到的Datatable欄位標題會自動化佈建為F1、F2等方式命名,與實際應用不符,所以當時是通過HDR=No方式將所有內容讀取到Datatable中,然後手動將第一行設定成標題的);IMEX ( IMport EXport mode )設定
IMEX 有三種模式:
0 is Export mode
1 is Import mode
2 is Linked mode (full update capabilities)
我這裡特別要說明的就是 IMEX 參數了,因為不同的模式代表著不同的讀寫行為:
當 IMEX=0 時為“匯出模式”,這個模式開啟的 Excel 檔案只能用來做“寫入”用途。
當 IMEX=1 時為“匯入模式”,這個模式開啟的 Excel 檔案只能用來做“讀取”用途。
當 IMEX=2 時為“連結模式”,這個模式開啟的 Excel 檔案可同時支援“讀取”與“寫入”用途。

---------------------------------

另外,讀取Excel2007版本的檔案時,版本應該從8.0改為12.0,同時驅動不能再用Jet,而應該用ACE。負責會造成“找不到可安裝的 ISAM”的錯誤。

---------------------------------

在網上還發現採用這種方式存在取出的Sheet表的個數多於實際Excel表中的Sheet表個數的情況,其原因有二:

1. 取出的名稱中,包括了XL命名管理器中的名稱(參見XL2007的公式--命名管理器, 快速鍵Crtl+F3);

2. 取出的名稱中,包括了FilterDatabase尾碼的, 這是XL用來記錄Filter範圍的。

對於第一點比較簡單, 刪除已有命名管理器中的內容即可;第二點處理起來比較麻煩, Filter刪除後這些名稱依然保留著,簡單的做法是新增Sheet然後將原Sheet Copy進去。但實際情況並不能為每個Excel做以上檢查。下面給出了過濾的方案。(此問題我們有驗證過,大家自己驗證一下吧)

//objConn為讀取Excel的連結,下面通過過濾來擷取有效Sheet頁名稱集合 System.Data.DataTable schemaTable = objConn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null); List<string> lstSheetNames = new List<string>(); for (int i = 0; i < schemaTable.Rows.Count; i++) {   string strSheetName = (string)dtSheetName.Rows[i]["TABLE_NAME"];   if (strSheetName.Contains("$") && !strSheetName.Replace("'", "").EndsWith("$"))   {     //過濾無效SheetName完畢....     continue;   }   if (lstSheetNames != null && !lstSheetNames.Contains(strSheetName))     lstSheetNames.Add(strSheetName); }

因為讀取出來無效SheetName一般情況最後一個字元都不會是$。如果SheetName有一些特殊符號,讀取出來的SheetName會自動加上單引號。比如在Excel中將SheetName編輯成MySheet(1),此時讀取出來的SheetName就為:'MySheet(1)$',所以判斷最後一個字元是不是$之前最好過濾一下單引號。

---------------------------------

(2)Com組件的方式(通過添加 Microsoft.Office.Interop.Excel引用實現)

優點:能夠非常靈活的讀取Excel中的資料,使用者可以靈活的調用各種函數進行處理。

缺點:基於儲存格的處理,讀取速度較慢,對於資料量較大的檔案最好不要使用此種方式讀取。

需要添加相應的DLL引用,必須存在此引用才可使用,如果是Web網站部署在IIS上時,還需要伺服器機子已安裝了Excel,有時候還需要為配置IIS許可權。

讀取代碼如下:

private Stopwatch wath = new Stopwatch();/// <summary>/// 使用COM讀取Excel/// </summary>/// <param name="excelFilePath">路徑</param>/// <returns>DataTabel</returns>public System.Data.DataTable GetExcelData(string excelFilePath){  Excel.Application app = new Excel.Application();  Excel.Sheets sheets;  Excel.Workbook workbook = null;  object oMissiong = System.Reflection.Missing.Value;  System.Data.DataTable dt = new System.Data.DataTable();  wath.Start();  try  {    if (app == null)    {      return null;    }    workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong,      oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);    //將資料讀入到DataTable中——Start     sheets = workbook.Worksheets;    Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//讀取第一張表    if (worksheet == null)      return null;    string cellContent;    int iRowCount = worksheet.UsedRange.Rows.Count;    int iColCount = worksheet.UsedRange.Columns.Count;    Excel.Range range;    //負責列頭Start    DataColumn dc;    int ColumnID = 1;    range = (Excel.Range)worksheet.Cells[1, 1];    while (range.Text.ToString().Trim() != "")    {      dc = new DataColumn();      dc.DataType = System.Type.GetType("System.String");      dc.ColumnName = range.Text.ToString().Trim();      dt.Columns.Add(dc);       range = (Excel.Range)worksheet.Cells[1, ++ColumnID];    }    //End    for (int iRow = 2; iRow <= iRowCount; iRow++)    {      DataRow dr = dt.NewRow();      for (int iCol = 1; iCol <= iColCount; iCol++)      {        range = (Excel.Range)worksheet.Cells[iRow, iCol];        cellContent = (range.Value2 == null) ? "" : range.Text.ToString();          dr[iCol - 1] = cellContent;      }      dt.Rows.Add(dr);    }    wath.Stop();    TimeSpan ts = wath.Elapsed;    //將資料讀入到DataTable中——End    return dt;  }  catch  {    return null;  }  finally  {    workbook.Close(false, oMissiong, oMissiong);    System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);    workbook = null;    app.Workbooks.Close();    app.Quit();    System.Runtime.InteropServices.Marshal.ReleaseComObject(app);    app = null;    GC.Collect();    GC.WaitForPendingFinalizers();  }}/// <summary>/// 使用COM,多線程讀取Excel(1 主線程、4 副線程)/// </summary>/// <param name="excelFilePath">路徑</param>/// <returns>DataTabel</returns>public System.Data.DataTable ThreadReadExcel(string excelFilePath){  Excel.Application app = new Excel.Application();  Excel.Sheets sheets = null;  Excel.Workbook workbook = null;  object oMissiong = System.Reflection.Missing.Value;  System.Data.DataTable dt = new System.Data.DataTable();  wath.Start();  try  {    if (app == null)    {      return null;    }    workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong,      oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);    //將資料讀入到DataTable中——Start     sheets = workbook.Worksheets;    Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//讀取第一張表    if (worksheet == null)      return null;    string cellContent;    int iRowCount = worksheet.UsedRange.Rows.Count;    int iColCount = worksheet.UsedRange.Columns.Count;    Excel.Range range;    //負責列頭Start    DataColumn dc;    int ColumnID = 1;    range = (Excel.Range)worksheet.Cells[1, 1];    while (iColCount >= ColumnID)    {      dc = new DataColumn();      dc.DataType = System.Type.GetType("System.String");      string strNewColumnName = range.Text.ToString().Trim();      if (strNewColumnName.Length == 0) strNewColumnName = "_1";      //判斷列名是否重複      for (int i = 1; i < ColumnID; i++)      {        if (dt.Columns[i - 1].ColumnName == strNewColumnName)          strNewColumnName = strNewColumnName + "_1";      }      dc.ColumnName = strNewColumnName;      dt.Columns.Add(dc);      range = (Excel.Range)worksheet.Cells[1, ++ColumnID];    }    //End    //資料大於500條,使用多進程進行讀取資料    if (iRowCount - 1 > 500)    {      //開始多線程讀取資料      //建立線程      int b2 = (iRowCount - 1) / 10;      DataTable dt1 = new DataTable("dt1");      dt1 = dt.Clone();      SheetOptions sheet1thread = new SheetOptions(worksheet, iColCount, 2, b2 + 1, dt1);      Thread othread1 = new Thread(new ThreadStart(sheet1thread.SheetToDataTable));      othread1.Start();      //阻塞 1 毫秒,保證第一個讀取 dt1      Thread.Sleep(1);      DataTable dt2 = new DataTable("dt2");      dt2 = dt.Clone();      SheetOptions sheet2thread = new SheetOptions(worksheet, iColCount, b2 + 2, b2 * 2 + 1, dt2);      Thread othread2 = new Thread(new ThreadStart(sheet2thread.SheetToDataTable));      othread2.Start();      DataTable dt3 = new DataTable("dt3");      dt3 = dt.Clone();      SheetOptions sheet3thread = new SheetOptions(worksheet, iColCount, b2 * 2 + 2, b2 * 3 + 1, dt3);      Thread othread3 = new Thread(new ThreadStart(sheet3thread.SheetToDataTable));      othread3.Start();      DataTable dt4 = new DataTable("dt4");      dt4 = dt.Clone();      SheetOptions sheet4thread = new SheetOptions(worksheet, iColCount, b2 * 3 + 2, b2 * 4 + 1, dt4);      Thread othread4 = new Thread(new ThreadStart(sheet4thread.SheetToDataTable));      othread4.Start();      //主線程讀取剩餘資料      for (int iRow = b2 * 4 + 2; iRow <= iRowCount; iRow++)      {        DataRow dr = dt.NewRow();        for (int iCol = 1; iCol <= iColCount; iCol++)        {          range = (Excel.Range)worksheet.Cells[iRow, iCol];          cellContent = (range.Value2 == null) ? "" : range.Text.ToString();          dr[iCol - 1] = cellContent;        }        dt.Rows.Add(dr);      }      othread1.Join();      othread2.Join();      othread3.Join();      othread4.Join();      //將多個線程讀取出來的資料追加至 dt1 後面      foreach (DataRow dr in dt.Rows)        dt1.Rows.Add(dr.ItemArray);      dt.Clear();      dt.Dispose();      foreach (DataRow dr in dt2.Rows)        dt1.Rows.Add(dr.ItemArray);      dt2.Clear();      dt2.Dispose();      foreach (DataRow dr in dt3.Rows)        dt1.Rows.Add(dr.ItemArray);      dt3.Clear();      dt3.Dispose();      foreach (DataRow dr in dt4.Rows)        dt1.Rows.Add(dr.ItemArray);      dt4.Clear();      dt4.Dispose();      return dt1;    }    else    {      for (int iRow = 2; iRow <= iRowCount; iRow++)      {        DataRow dr = dt.NewRow();        for (int iCol = 1; iCol <= iColCount; iCol++)        {          range = (Excel.Range)worksheet.Cells[iRow, iCol];          cellContent = (range.Value2 == null) ? "" : range.Text.ToString();          dr[iCol - 1] = cellContent;        }        dt.Rows.Add(dr);      }    }    wath.Stop();    TimeSpan ts = wath.Elapsed;    //將資料讀入到DataTable中——End    return dt;  }  catch  {    return null;  }  finally  {    workbook.Close(false, oMissiong, oMissiong);    System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);    System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);    workbook = null;    app.Workbooks.Close();    app.Quit();    System.Runtime.InteropServices.Marshal.ReleaseComObject(app);    app = null;    GC.Collect();    GC.WaitForPendingFinalizers();  }}

(3)NPOI方式讀取Excel(此方法未經過測試)

NPOI 是 POI 項目的 .NET 版本。POI是一個開源的Java讀寫Excel、WORD等微軟OLE2組件文檔的項目。使用 NPOI 你就可以在沒有安裝 Office 或者相應環境的機器上對 WORD/EXCEL 文檔進行讀寫。

優點:讀取Excel速度較快,讀取方式操作靈活性

缺點:需要下載相應的外掛程式並添加到系統引用當中。

/// <summary>/// 將excel中的資料匯入到DataTable中/// </summary>/// <param name="sheetName">excel工作薄sheet的名稱</param>/// <param name="isFirstRowColumn">第一行是否是DataTable的列名</param>/// <returns>返回的DataTable</returns>public DataTable ExcelToDataTable(string sheetName, bool isFirstRowColumn){  ISheet sheet = null;  DataTable data = new DataTable();  int startRow = 0;  try  {    fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);    if (fileName.IndexOf(".xlsx") > 0) // 2007版本      workbook = new XSSFWorkbook(fs);    else if (fileName.IndexOf(".xls") > 0) // 2003版本      workbook = new HSSFWorkbook(fs);    if (sheetName != null)    {      sheet = workbook.GetSheet(sheetName);    }    else    {      sheet = workbook.GetSheetAt(0);    }    if (sheet != null)    {      IRow firstRow = sheet.GetRow(0);      int cellCount = firstRow.LastCellNum; //一行最後一個cell的編號 即總的列數      if (isFirstRowColumn)      {        for (int i = firstRow.FirstCellNum; i < cellCount; ++i)        {          DataColumn column = new DataColumn(firstRow.GetCell(i).StringCellValue);          data.Columns.Add(column);        }        startRow = sheet.FirstRowNum + 1;      }      else      {        startRow = sheet.FirstRowNum;      }      //最後一列的標號      int rowCount = sheet.LastRowNum;      for (int i = startRow; i <= rowCount; ++i)      {        IRow row = sheet.GetRow(i);        if (row == null) continue; //沒有資料的行預設是null                        DataRow dataRow = data.NewRow();        for (int j = row.FirstCellNum; j < cellCount; ++j)        {          if (row.GetCell(j) != null) //同理,沒有資料的儲存格都預設是null            dataRow[j] = row.GetCell(j).ToString();        }        data.Rows.Add(dataRow);      }    }    return data;  }  catch (Exception ex)  {    Console.WriteLine("Exception: " + ex.Message);    return null;  }}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.