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中的所有列都转为字符型。

  CSV的文件,它的连接字符串会读取该文件所在的文件夹下所有的文本文件数据。

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.GetDirectoryName(filePath)};Extended Properties='Text;FMT=Delimited;HDR={hdr};FMT=Delimited;'";
                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);
        
    }

}

 

posted @ 2020-09-11 13:44  一只独行的猿  阅读(1743)  评论(0编辑  收藏  举报