C# 使用 Aspose.Cells 库读写 Excel 与 CSV 文件的通用实现

Chloe
Chloe
发布时间:2026-10-14| 6人浏览过
原创

在使用 C# 进行数据处理时,经常需要将结构化数据在内存对象与 Excel/CSV 文件之间转换。借助第三方组件 Aspose.Cells,可以通过泛型与反射技术,实现不依赖具体业务类的通用读写方法。本文将展示如何定义通用的读取接口(支持 CSV 和 xlsx)以及批量写入逻辑,涵盖文件校验、动态属性映射、数据类型转换及结果保存等核心步骤。

实现目标与准备

该方案主要解决两个问题:一是从文件或流中读取表格数据并将其转换为强类型对象列表;二是将对象列表直接导出为标准电子表格格式。为了确保代码的通用性,输入的文件路径需包含完整的扩展名(.csv 或 .xlsx)。在调用任何方法之前,请确保项目中已引用 Aspose.Cells 命名空间。

读取 Excel/CSV 数据至对象列表

第1步:构建泛型读取方法

此方法的目的是根据传入的 T 类型,自动匹配 Excel 第一行的列名(即表头),将每一行数据实例化并为对应属性赋值。若表头名称与类属性名一致(不区分大小写),即可自动填充。

  1. 定义一个静态或实例方法 GetObjectList<T>,其中泛型约束为 where T : new(),以保证能够创建无参实例。
  2. 对传入的 filePath 进行合法性检查。仅允许以 .csv 或 .xlsx 结尾的路径,否则直接返回空集合。
  3. 初始化 Workbook 实例加载源文件,并获取第一个工作表 Worksheet。
  4. 调用辅助方法 GetTitleDic(sheet),遍历表头行(第0行),建立 Dictionary<string, int>,键为列名,值为对应的列索引。

第2步:逐行解析与数据转换

从第1行开始循环遍历工作表中的行:

  • 终止条件:如果当前行首列([i, 0])内容为空,则说明数据结束,跳出循环。
  • 获取当前对象的类型信息 Type type = typeof(T),并通过 type.GetProperties() 获取所有公开属性。
  • 遍历每个属性,若在标题字典中存在对应的列名,则获取该单元格文本值。
  • 类型转换逻辑:
    • 若属性类型为 string,直接使用单元格文本值。
    • 若属性类型为 Enum,尝试将文本值转换为整型后赋值给枚举字段。
    • 对于其他类型,优先查找该类型是否存在名为 Parse 且参数为 string 的方法,通过反射调用该方法进行解析(例如 int, DateTime 等)。
      • 如果在解析过程中抛出异常,则忽略该行数据在该属性的赋值(可根据需求调整错误处理策略,如记录日志)。
  • 将生成的实例加入列表,最后返回整个对象列表。
  • /// <summary>
    /// 从Excel获取数据
    /// </summary>
    /// <typeparam name="T">对象</typeparam>
    /// <param name="filePath">文件完整路径</param>
    /// <returns>对象列表</returns>
    public List<T> GetObjectList<T>(string filePath) where T : new()
    {
        List<T> list = new List<T>();
        // 限制文件格式
        if (!filePath.Trim().EndsWith("csv") && !filePath.Trim().EndsWith("xlsx"))
        {
            return list;
        }
    
        Type type = typeof(T);
        Workbook workbook = new Workbook(filePath);
        Worksheet sheet = workbook.Worksheets[0];
    
        // 获取标题列名与索引的映射
        var titleDic = GetTitleDic(sheet);
    
        // 从第二行(索引1)开始逐行读取
        for (int i = 1; i < int.MaxValue; i++)
        {
            // 遇到首列为空的行,判定数据结束
            if (string.IsNullOrEmpty(sheet.Cells[i, 0].StringValue))
            {
                break;
            }
    
            T instance = new T();
    
            // 遍历当前对象的所有属性进行赋值
            foreach (var item in type.GetProperties())
            {
                if (titleDic.ContainsKey(item.Name))
                {
                    string str = sheet.Cells[i, titleDic[item.Name]].StringValue;
                    if (!string.IsNullOrEmpty(str))
                    {
                        try
                        {
                            if (item.PropertyType == typeof(string))
                            {
                                item.SetValue(instance, str);
                            }
                            else if (item.PropertyType.IsEnum)
                            {
                                item.SetValue(instance, int.Parse(str));
                            }
                            else
                            {
                                MethodInfo method = item.PropertyType.GetMethod("Parse", new Type[] { typeof(string) });
                                if (method != null)
                                {
                                    object obj = method.Invoke(null, new object[] { str });
                                    item.SetValue(instance, obj);
                                }
                            }
                        }
                        catch (Exception)
                        {
                            // 此处可以捕获异常并根据需要记录日志,避免单行错误导致整体崩溃
                        }
                    }
                }
            }
            list.Add(instance);
        }
        return list;
    }

    将对象列表导出为 Excel 文件

    第3步:编写通用导出方法

    此方法的目的是将任意对象列表序列化并输出到指定的文件中。首先检查文件后缀是否为 .csv 或 .xlsx,若不合法则直接返回。
    接着创建新的 Workbook 实例,锁定默认的第一个工作表 Worksheet。

    第4步:冻结首行与动态表头生成

    • 设置冻结窗格:调用 sheet.FreezePanes(1, 1, 1, 0),这将使首行(表头)在向下滚动时保持固定。
    • 当遍历到列表的第一个对象(row == 0)时,执行表头写入:
      • 再次通过反射获取对象的属性集合。
      • 注意此处使用了 BindingFlags 组合以精确控制获取范围,但在示例中保留了原有的基础写法:遍历每一个属性,将属性名写入当前行的每一列。

    第5步:逐行列入数据并保存

    • 使用外层循环遍历对象列表,内层循环遍历当前对象的属性:
      • 外层变量 row 记录行号,内层变量 column 记录列号。
      • 调用 property.GetValue(obj).ToString() 获取属性值并将其转为字符串填入对应单元格。
    • 每写完一行对象数据,row++。
    • 循环结束后,调用 workbook.Save(saveFilePath) 将内存中的结构持久化为文件。
    /// <summary>
    /// 把对象List保存到Excel
    /// </summary>
    /// <typeparam name="T">对象</typeparam>
    /// <param name="objList">对象列表</param>
    /// <param name="saveFilePath">保存文件的完整路径,包括文件类型</param>
    public void SetExcelList<T>(List<T> objList, string saveFilePath)
    {
        // 校验保存路径的格式要求
        if (!saveFilePath.Trim().EndsWith("csv") && !saveFilePath.Trim().EndsWith("xlsx"))
        {
            return;
        }
    
        Workbook workbook = new Workbook();
        Worksheet sheet = workbook.Worksheets[0];
    
        // 冻结第一行以便阅读
        sheet.FreezePanes(1, 1, 1, 0);
    
        int row = 0;
        foreach (var obj in objList)
        {
            int column = 0;
            // 获取指定绑定标志的属性集合
            var properties = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly);
            
            // 如果是第一行,负责写入列标题
            if (row == 0)
            {
                foreach (var titName in properties)
                {
                    sheet.Cells[0, column].PutValue(titName.Name);
                    column++;
                }
                row++;
            }
    
            // 写入当前行对象的属性值
            column = 0;
            foreach (var property in properties)
            {
                var itemValue = property.GetValue(obj);
                sheet.Cells[row, column].PutValue(itemValue.ToString());
                column++;
            }
            row++;
        }
        workbook.Save(saveFilePath);
    }
    
    /// <summary>
    /// 辅助方法:获取标题行数据
    /// </summary>
    private Dictionary<string, int> GetTitleDic(Worksheet sheet)
    {
        Dictionary<string, int> titList = new Dictionary<string, int>();
        // 遍历第一行找出非空的列作为键名
        for (int i = 0; i < int.MaxValue; i++)
        {
            if (sheet.Cells[0, i].StringValue == string.Empty)
            {
                return titList;
            }
            titList.Add(sheet.Cells[0, i].StringValue, i);
        }
        return titList;
    }

    注意事项

    • 路径校验:代码中强制要求路径必须以 .csv 或 .xlsx 结尾,这是为了避免在解析未知格式时发生不可预知的行为。
    • 异常处理:在 GetObjectList 内部使用了 try-catch 包裹单个属性的解析过程。这意味着如果某一行的某一列数据格式有误,该特定字段的赋值会失败,但不会中断整个数据的读取过程。
    • 性能考量:由于大量使用了反射(Reflection),对于超大规模数据集(例如上万行以上的大表),读写速度可能不及特定的底层二进制读写器。但对于常规业务报表或小规模配置文件的处理,该方法提供了极佳的灵活性。
    以上就是 C# 使用 Aspose.Cells 读写 Excel 文件的详细内容,更多关于 C# 文件操作的技术文章请关注本站其它相关文章!
    相关标签:

    免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

    Python 相关产品和应用

    全部下载
    Selenium

    Selenium

    4.51.0 · Windows

    用于浏览器自动化和 Web 测试的 Python 工具。

    scikit-learn

    scikit-learn

    1.9.1 · Windows

    常用 Python 机器学习算法与模型评估工具库。

    Black

    Black

    26.10.1 · Windows

    强调一致性的 Python 自动代码格式化工具。

    Ruff

    Ruff

    0.17.1 · macOS

    由 Rust 编写的高速 Python 代码检查和格式化工具。

    pytest

    pytest

    9.1.1 · Windows

    简洁而强大的 Python 自动化测试框架。

    FastAPI

    FastAPI

    0.146.0 · Windows

    高性能 Python API 框架,支持类型提示和自动接口文档。

    Flask

    Flask

    3.1.3 · Windows

    轻量灵活的 Python Web 应用框架。

    Django

    Django

    6.1.2 · Windows

    成熟的 Python Web 框架,适合构建内容站点和业务系统。

    Spyder

    Spyder

    6.1.7 · Windows

    面向科学计算与数据分析的 Python 集成开发环境。

    SciPy

    SciPy

    1.18.1 · Windows

    面向科学、工程和技术计算的 Python 算法库。