1. 程式人生 > 實用技巧 >Excel檔案讀取操作方法

Excel檔案讀取操作方法

  Excel的連線中,由兩個值需要注意。

  首先是HDR值,該值指示是否將表中的第一行有效(第一個行資料不為空的行)資料當作標題列處理。如果選擇是YES,那麼通過C#讀取出來的資料表中,表的列標題則是對應的第一行有效資料;否則,將所有資料都當作資料處理,此時以F1、F2……Fn為列標題。預設的是YES,見後續的使用。

  其次是IMEX模式,IMEX 有三種模式,不同的模式代表著不同的讀寫行為:

  • 0 is Export mode:為“匯出模式”,這個模式開啟的 Excel 檔案只能用來做“寫入”用途。 ---輸出模式;
  • 1 is Import mode:為“匯入模式”,這個模式開啟的 Excel 檔案只能用來做“讀取”用途。---輸入模式,始終將“互混”資料列作為文字讀取;
  • 2 is Linked mode (full update capabilities):為“連結模式”,這個模式開啟的 Excel 檔案可同時支援“讀取”與“寫入”用途。---連結模式(完全更新能力,效率不高)

  IMEX=1的作用是,當讀取Excel中每個單元格的值到DataTable中的時候,不管其在Excel單元格時候是什麼資料型別,賦值到DataTable中都強制轉化為字串型別。

  在沒有IMEX=1這個屬性的時候,預設的是根據Excel中對應Column的資料型別來決定DataTable中Column的資料型別。這種情況在Excel中某一列的資料型別都是一致的情況下沒有問題,是什麼型別,就會在DataTable中的對應列設定相應的型別。但是如果Excel中這一列的型別混亂的話,比如說既包括數值型又有字串型,在執行時建立DataTable的時候,會去先判斷Excel中這一列哪種型別的資料佔主體,然後給DataTable的列設定為這種型別。比如說,如果一列中既有整數型又有字元型,而整數型單元格佔主體,這時DataTable中的列就是整數型。

  這時又出現另外一個問題,當要把值寫入到DataTable的時候,如果該單元格符合DataTable中要求的型別,就會寫入,如果不符合的話,系統會去強制轉換。比如,如果Excel中是字串的5,而該單元格所在的列整數型佔主體,DataTable中這一列是數值型,這時,系統會把字串的5強制轉為數值型的5然後賦給DataTable相應的地方。但是,此時,如果轉換有問題的話,比如,此列中有一單元格中是“NO5”,這時強制轉換就會有問題,系統就會給DataTable相應的地方賦值DBNull,現在如果你用DataGridView來顯示那個DataTable的時候,這個地方顯示出來就是一個空白的格,但要注意的是,並不是DataTable中的這一行這一列是null,而是DBNull.其實,我覺得,跟null的作用是一樣的,反正在DataGridView中是不會顯示出來。只是我們在寫程式如果需要對DataTable的null元素篩選的話,需要注意這個問題。

  如果Excel中,某一行字串型別佔主體的話,那麼DataTable中這一列就會設定為字串型,而且任何型別都能順利轉換成字串型別,所以,Excel的類會完整的顯示出來,不管這一列中的字串型別的單元格,還是整數型的單元格,都能完整的顯示出來。這是一很特別的地方。但這僅僅是一個特例。

  所以,如果為了處理的時候資料型別的一致性,如果Excel中資料型別混亂的話,可以使用IMEX=1使DataTable中的所有列都轉為字元型。

ExcelReader程式碼:

/// <summary>
/// 連線Excel的模式
/// </summary>
public enum IMEX
{
    /// <summary>
    /// 匯出(輸出)模式
    /// </summary>
    Export = 0,
    /// <summary>
    /// 匯入(輸入)模式
    /// </summary>
    Import = 1,
    /// <summary>
    /// 連結模式(完全更新能力)
    /// </summary>
    Linked = 2,
}
public sealed class ExcelReader
{

    /// <summary>
    /// 是否將第一行資料作為標題列。如果選擇no, 則將第一行資料作為資料讀取,則預設F1~開始為標題
    /// </summary>
    public bool IsFirstRowAsColumnNames { get; set; } = true;
    /// <summary>
    /// 連線Excel的模式
    /// </summary>
    public IMEX IMEX { get; set; } = IMEX.Import;
    /// <summary>
    /// 檔案的絕對路徑
    /// </summary>
    string filePath = string.Empty;

    /// <summary>
    /// 構造物件
    /// </summary>
    /// <param name="hdr">第一行的讀取模式:預設是Yes</param>
    /// <param name="imex"></param>
    public ExcelReader(IMEX imex = IMEX.Import)
    {
        IMEX = imex;
    }
    


    FileType fileType = FileType.noset;
    /// <summary>
    /// 獲取連線字串
    /// </summary>
    /// <param name="fileFullPath">檔案的絕對路徑</param>
    /// <returns>連線字串</returns>
    public string GetConnectString(string fileFullPath)
    {
        fileType = FileType.noset;
        if (!File.Exists(fileFullPath))//判斷檔案是否存在
        {
            throw new FileNotFoundException(fileFullPath);
        }
        string hdr = IsFirstRowAsColumnNames ? "YES" : "NO";
        string connString = string.Empty;
        filePath = fileFullPath;
        switch (Path.GetExtension(filePath))
        {
            case ".xls":
                connString = $"Provider=;Data Source={ filePath }; Extended Properties='Excel 8.0;HDR={hdr};IMEX={(int)IMEX};'";
                fileType = FileType.xls;
                break;
            case ".xlsx":
                connString = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={ filePath };Extended Properties='Excel 12.0;HDR={hdr};IMEX={(int)IMEX};'";
                fileType = FileType.xlsx;
                break;
            case ".csv"://filePath.Remove(filePath.LastIndexOf("\\") + 1)
                connString = $"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={Path.GetFileNameWithoutExtension(filePath)};Extended Properties='Text;FMT=Delimited;HDR={hdr};IMEX={(int)IMEX};'";
                fileType = FileType.csv;
                break;
        }
        return connString;
    }

    /// <summary>
    /// 開啟連線
    /// </summary>
    /// <param name="fileFullPath">檔案的絕對路徑</param>
    /// <returns>連線物件</returns>
    public OleDbConnection GetConnection(string fileFullPath)
    {
        string connectString = GetConnectString(fileFullPath);//獲取檔案連線字串
        if (fileType == FileType.noset)
        {
            throw new ArgumentException("Incorrect file type.");
        }
        OleDbConnection oleDb = new OleDbConnection();
        try
        {
            oleDb.ConnectionString = connectString;
        }
        catch (Exception ex)
        {
            oleDb.Close();
            throw ex;
        }
        return oleDb;
    }


    /// <summary>
    /// 獲取所有表單的名稱;注意,表單名順序不一定是Excel中顯示的順序,是新增表時的先後順序
    /// </summary>
    /// <param name="fileFullName">檔案全名</param>
    /// <returns>表單名稱</returns>
    public List<string> GetSheetsName(string fileFullName)
    {
        OleDbConnection connection = GetConnection(fileFullName);//獲取連線物件
        List<string> lstSheets = GetSheetsName(connection);
        return lstSheets;
    }

    /// <summary>
    /// 獲取所有表單的名稱
    /// </summary>
    /// <param name="connection">連線物件</param>
    /// <returns>表單名稱</returns>
    public List<string> GetSheetsName(OleDbConnection connection)
    {
        List<string> lstSheets = new List<string>();
        try
        {
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            DataTable dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);//獲取表單
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                lstSheets.Add(dt.Rows[i]["Table_Name"].ToString());
            }
        }
        finally
        {
            connection.Close();
        }

        return lstSheets;
    }

    /// <summary>
    /// 獲取檔案內的所有Table資料集合
    /// </summary>
    /// <param name="fileFullName">檔案全名</param>
    /// <returns>資料集合</returns>
    public DataSet GetDataSet(string fileFullName)
    {
        OleDbConnection connection = GetConnection(fileFullName);//獲取連線物件
        return GetDataSet(connection);
    }

    /// <summary>
    /// 獲取檔案內的所有Table資料集合
    /// </summary>
    /// <param name="connection">連線物件</param>
    /// <returns>資料集合</returns>
    public DataSet GetDataSet(OleDbConnection connection)
    {
        DataSet ds = new DataSet();
        try
        {
            List<string> lstSheets = GetSheetsName(connection);
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            OleDbDataAdapter adapter = new OleDbDataAdapter(connection.CreateCommand());//建立讀取資料
            for (int i = 0; i < lstSheets.Count; i++)
            {
                adapter.SelectCommand.CommandText = string.Format("SELECT * FROM [{0}]", lstSheets[i].Trim('/'));//查詢字串
                adapter.Fill(ds);//填充資料
                ds.Tables[i].TableName = lstSheets[i];//賦值表名
            }
            adapter.Dispose();
        }
        finally
        {
            connection.Close();
        }
        return ds;
    }

    /// <summary>
    /// 獲取檔案內的所有Table資料集合
    /// </summary>
    /// <param name="fileFullName">檔案全名</param>
    /// <param name="tableName">表名</param>
    /// <returns>資料集合</returns>
    public DataTable GetDataTable(string fileFullName, string tableName)
    {
        OleDbConnection connection = GetConnection(fileFullName);//獲取連線物件
        return GetDataTable(connection, tableName);
    }

    /// <summary>
    /// 獲取檔案內的所有Table資料集合
    /// </summary>
    /// <param name="connection">連線物件</param>
    /// <param name="tableName">表名</param>
    /// <returns>資料集合</returns>
    public DataTable GetDataTable(OleDbConnection connection, string tableName)
    {
        DataSet ds = new DataSet();
        try
        {
            if (!tableName.EndsWith("$"))//檢查sheet名稱是否是以'$'結尾的,必須以'$'結尾
            {
                tableName += '$';
            }
            connection.Open();
            OleDbCommand cmd = connection.CreateCommand();
            cmd.CommandText = $"Select * from [{tableName}]";//必須加方括號
            using (OleDbDataReader reader = cmd.ExecuteReader())
            {
                ds.Load(reader, LoadOption.OverwriteChanges, tableName);//這兒使用的是DataSet的載入方法
            }
        }
        finally
        {
            connection.Close();
        }
        if (ds.Tables.Count > 0)
        {
            return ds.Tables[0];
        }
        else
        {
            return null;
        }
    }

    /// <summary>
    /// 通過sql語句獲取表單
    /// </summary>
    /// <param name="fileFullPath">檔案全路徑名稱</param>
    /// <param name="sql">sql語句</param>
    /// <returns>通過sql查詢到的表單</returns>
    /// <remarks>
    /// 如果HDR使用的是NO, 則預設F1型別開始為列標題(相當於資料庫的欄位名);
    /// 如果HDR使用的是Yes,則第一行的資料值當作資料庫的欄位名型別
    /// string sql="select  F1,F2  from [Sheet1$] ";//注意表名稱[] 和 $ 都不能少了
    /// </remarks>
    public DataTable GetDataTableBySql(string fileFullPath, string sql)
    {
        OleDbConnection connection = GetConnection(fileFullPath);
        return GetDataTableBySql(connection, sql);
    }

    /// <summary>
    /// 通過sql語句查詢表單
    /// </summary>
    /// <param name="connection">連線物件</param>
    /// <param name="sql">查詢語句</param>
    /// <returns>通過sql查詢到的表單</returns>
    /// <remarks>
    /// 如果HDR使用的是NO, 則預設F1型別開始為列標題(相當於資料庫的欄位名)
    /// string sql="select  F1,F2  from [Sheet1$] ";//注意表名稱[] 和 $ 都不能少了
    /// </remarks>
    public DataTable GetDataTableBySql(OleDbConnection connection, string sql)
    {
        DataSet ds = new DataSet();
        try
        {
            connection.Open();
            OleDbDataAdapter adapter = new OleDbDataAdapter(sql, connection);
            adapter.Fill(ds);
        }
        finally
        {
            connection.Close();
        }
        if (ds.Tables.Count > 0)
        {
            return ds.Tables[0];
        }
        else
        {
            return null;
        }
    }

    /// <summary>
    /// 檔案型別
    /// </summary>
    private enum FileType
    {
        noset,
        xls,
        xlsx,
        csv
    }
}

ExcelReader的使用:

private void button1_Click(object sender, EventArgs e)
{
    OpenFileDialog ofd = new OpenFileDialog();
    ofd.Filter = "Execl files (*.xlsx)|*.xlsx|(*.xls)|*.xls*";

    if(ofd.ShowDialog()== DialogResult.OK)
    {
        ExcelReader excel = new ExcelReader();
        List<string> lstSheets = excel.GetSheetsName(ofd.FileName);//獲取所有表名
        dataGridView1.DataSource = lstSheets;                
        
        DataSet ds0= excel.GetDataSet(ofd.FileName);//獲取Excel檔案中的所有資料

        DataTable table = excel.GetDataTable(ofd.FileName,lstSheets.First());//讀取Excel中第一張Sheet的值

        //excel.IsFirstRowAsColumnNames =true;
        //string sql = $"select  學號,姓名,性別,備註  from [{lstSheets[0]}] where 姓名='張三'";
        excel.IsFirstRowAsColumnNames = false;
        //string sql =$"select  F1,F2,F3,F6  from [{lstSheets[0]}] where F2='張三'";//查詢張三 
        string sql = $"select  *  from [{lstSheets[0]}]";//查詢張三 
        dataGridView1.DataSource = excel.GetDataTableBySql(ofd.FileName, sql);
        
    }

}