C# 读取 CSV/Excel 文件数据至 DataGridView

C# 用于读取 CSV 和 Excel 文件数据并显示在 DataGridView 控件中,支持文件选择、数据预览、格式处理和错误处理等功能。

解决方案结构

复制代码
CsvExcelReader/
├── CsvExcelReader.sln
├── CsvExcelReader/
│   ├── App.config
│   ├── Form1.cs
│   ├── Form1.Designer.cs
│   ├── Form1.resx
│   ├── Program.cs
│   ├── FileReader.cs
│   ├── DataProcessor.cs
│   └── Properties/
│       ├── AssemblyInfo.cs
│       └── Resources.Designer.cs
└── packages.config

完整代码实现

1. 主窗体 (Form1.cs)

csharp 复制代码
using System;
using System.Data;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.HSSF.UserModel;

namespace CsvExcelReader
{
    public partial class MainForm : Form
    {
        private readonly FileReader fileReader = new FileReader();
        private readonly DataProcessor dataProcessor = new DataProcessor();
        
        public MainForm()
        {
            InitializeComponent();
            InitializeUI();
        }

        private void InitializeUI()
        {
            // 窗体设置
            this.Text = "CSV/Excel 数据读取器";
            this.Size = new Size(900, 600);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.BackColor = Color.FromArgb(240, 240, 240);
            
            // 创建控件
            lblTitle = new Label
            {
                Text = "CSV/Excel 数据读取器",
                Font = new Font("微软雅黑", 16, FontStyle.Bold),
                ForeColor = Color.DarkSlateBlue,
                AutoSize = true,
                Location = new Point(20, 20)
            };
            
            btnOpenCsv = new Button
            {
                Text = "打开 CSV 文件",
                Size = new Size(120, 40),
                Location = new Point(30, 70),
                BackColor = Color.SteelBlue,
                ForeColor = Color.White,
                FlatStyle = FlatStyle.Flat,
                Font = new Font("微软雅黑", 10)
            };
            btnOpenCsv.FlatAppearance.BorderSize = 0;
            btnOpenCsv.Click += BtnOpenCsv_Click;
            
            btnOpenExcel = new Button
            {
                Text = "打开 Excel 文件",
                Size = new Size(120, 40),
                Location = new Point(160, 70),
                BackColor = Color.ForestGreen,
                ForeColor = Color.White,
                FlatStyle = FlatStyle.Flat,
                Font = new Font("微软雅黑", 10)
            };
            btnOpenExcel.FlatAppearance.BorderSize = 0;
            btnOpenExcel.Click += BtnOpenExcel_Click;
            
            btnSaveCsv = new Button
            {
                Text = "保存为 CSV",
                Size = new Size(120, 40),
                Location = new Point(290, 70),
                BackColor = Color.Orange,
                ForeColor = Color.White,
                FlatStyle = FlatStyle.Flat,
                Font = new Font("微软雅黑", 10),
                Enabled = false
            };
            btnSaveCsv.FlatAppearance.BorderSize = 0;
            btnSaveCsv.Click += BtnSaveCsv_Click;
            
            btnSaveExcel = new Button
            {
                Text = "保存为 Excel",
                Size = new Size(120, 40),
                Location = new Point(420, 70),
                BackColor = Color.Purple,
                ForeColor = Color.White,
                FlatStyle = FlatStyle.Flat,
                Font = new Font("微软雅黑", 10),
                Enabled = false
            };
            btnSaveExcel.FlatAppearance.BorderSize = 0;
            btnSaveExcel.Click += BtnSaveExcel_Click;
            
            btnClear = new Button
            {
                Text = "清除数据",
                Size = new Size(120, 40),
                Location = new Point(550, 70),
                BackColor = Color.Gray,
                ForeColor = Color.White,
                FlatStyle = FlatStyle.Flat,
                Font = new Font("微软雅黑", 10)
            };
            btnClear.FlatAppearance.BorderSize = 0;
            btnClear.Click += BtnClear_Click;
            
            // 状态标签
            lblStatus = new Label
            {
                Text = "就绪",
                AutoSize = true,
                Location = new Point(30, 120),
                Font = new Font("微软雅黑", 9),
                ForeColor = Color.DimGray
            };
            
            // 数据网格视图
            dataGridView = new DataGridView
            {
                Location = new Point(20, 150),
                Size = new Size(840, 400),
                BackgroundColor = Color.White,
                BorderStyle = BorderStyle.FixedSingle,
                AllowUserToAddRows = false,
                AllowUserToDeleteRows = false,
                ReadOnly = true,
                AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells,
                RowHeadersVisible = false,
                ColumnHeadersDefaultCellStyle = new DataGridViewCellStyle
                {
                    BackColor = Color.SteelBlue,
                    ForeColor = Color.White,
                    Font = new Font("微软雅黑", 10, FontStyle.Bold)
                },
                DefaultCellStyle = new DataGridViewCellStyle
                {
                    Font = new Font("微软雅黑", 9),
                    SelectionBackColor = Color.LightSteelBlue
                },
                EnableHeadersVisualStyles = false
            };
            
            // 进度条
            progressBar = new ProgressBar
            {
                Location = new Point(20, 560),
                Size = new Size(840, 20),
                Visible = false
            };
            
            // 添加控件到窗体
            this.Controls.Add(lblTitle);
            this.Controls.Add(btnOpenCsv);
            this.Controls.Add(btnOpenExcel);
            this.Controls.Add(btnSaveCsv);
            this.Controls.Add(btnSaveExcel);
            this.Controls.Add(btnClear);
            this.Controls.Add(lblStatus);
            this.Controls.Add(dataGridView);
            this.Controls.Add(progressBar);
        }

        #region 控件声明
        private Label lblTitle;
        private Button btnOpenCsv;
        private Button btnOpenExcel;
        private Button btnSaveCsv;
        private Button btnSaveExcel;
        private Button btnClear;
        private Label lblStatus;
        private DataGridView dataGridView;
        private ProgressBar progressBar;
        #endregion

        #region 事件处理
        private void BtnOpenCsv_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*";
                openFileDialog.Title = "选择 CSV 文件";
                
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    LoadFile(openFileDialog.FileName, FileType.Csv);
                }
            }
        }

        private void BtnOpenExcel_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "Excel 文件 (*.xls;*.xlsx)|*.xls;*.xlsx|所有文件 (*.*)|*.*";
                openFileDialog.Title = "选择 Excel 文件";
                
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    LoadFile(openFileDialog.FileName, FileType.Excel);
                }
            }
        }

        private void BtnSaveCsv_Click(object sender, EventArgs e)
        {
            if (dataGridView.DataSource == null) return;
            
            using (SaveFileDialog saveFileDialog = new SaveFileDialog())
            {
                saveFileDialog.Filter = "CSV 文件 (*.csv)|*.csv";
                saveFileDialog.Title = "保存为 CSV 文件";
                saveFileDialog.FileName = "data_export.csv";
                
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        dataProcessor.ExportToCsv((DataTable)dataGridView.DataSource, saveFileDialog.FileName);
                        lblStatus.Text = $"数据已成功导出到: {saveFileDialog.FileName}";
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"导出失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void BtnSaveExcel_Click(object sender, EventArgs e)
        {
            if (dataGridView.DataSource == null) return;
            
            using (SaveFileDialog saveFileDialog = new SaveFileDialog())
            {
                saveFileDialog.Filter = "Excel 文件 (*.xlsx)|*.xlsx";
                saveFileDialog.Title = "保存为 Excel 文件";
                saveFileDialog.FileName = "data_export.xlsx";
                
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        dataProcessor.ExportToExcel((DataTable)dataGridView.DataSource, saveFileDialog.FileName);
                        lblStatus.Text = $"数据已成功导出到: {saveFileDialog.FileName}";
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"导出失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void BtnClear_Click(object sender, EventArgs e)
        {
            dataGridView.DataSource = null;
            btnSaveCsv.Enabled = false;
            btnSaveExcel.Enabled = false;
            lblStatus.Text = "数据已清除";
        }
        #endregion

        #region 文件加载与处理
        private void LoadFile(string filePath, FileType fileType)
        {
            try
            {
                progressBar.Visible = true;
                progressBar.Style = ProgressBarStyle.Marquee;
                lblStatus.Text = $"正在加载文件: {Path.GetFileName(filePath)}...";
                Application.DoEvents();
                
                DataTable dataTable = fileType == FileType.Csv 
                    ? fileReader.ReadCsv(filePath) 
                    : fileReader.ReadExcel(filePath);
                
                if (dataTable == null || dataTable.Rows.Count == 0)
                {
                    lblStatus.Text = "文件为空或未找到数据";
                    return;
                }
                
                // 数据预处理
                dataTable = dataProcessor.ProcessDataTable(dataTable);
                
                // 绑定到DataGridView
                dataGridView.DataSource = dataTable;
                
                // 启用保存按钮
                btnSaveCsv.Enabled = true;
                btnSaveExcel.Enabled = true;
                
                lblStatus.Text = $"已加载: {Path.GetFileName(filePath)} | 行: {dataTable.Rows.Count} | 列: {dataTable.Columns.Count}";
            }
            catch (Exception ex)
            {
                MessageBox.Show($"加载文件失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                lblStatus.Text = $"错误: {ex.Message}";
            }
            finally
            {
                progressBar.Visible = false;
            }
        }
        #endregion
    }

    public enum FileType
    {
        Csv,
        Excel
    }
}

2. 文件读取器 (FileReader.cs)

csharp 复制代码
using System;
using System.Data;
using System.IO;
using System.Text;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.HSSF.UserModel;

namespace CsvExcelReader
{
    public class FileReader
    {
        /// <summary>
        /// 读取CSV文件并返回DataTable
        /// </summary>
        public DataTable ReadCsv(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException("文件不存在", filePath);
            
            DataTable dataTable = new DataTable();
            int rowCount = 0;
            
            using (var reader = new StreamReader(filePath, Encoding.UTF8))
            {
                string line;
                bool isFirstRow = true;
                
                while ((line = reader.ReadLine()) != null)
                {
                    // 处理可能的引号包围字段
                    string[] fields = ParseCsvLine(line);
                    
                    if (isFirstRow)
                    {
                        // 创建列
                        foreach (string field in fields)
                        {
                            dataTable.Columns.Add(field);
                        }
                        isFirstRow = false;
                    }
                    else
                    {
                        // 添加行数据
                        if (fields.Length > dataTable.Columns.Count)
                        {
                            // 添加缺失的列
                            for (int i = dataTable.Columns.Count; i < fields.Length; i++)
                            {
                                dataTable.Columns.Add($"Column{dataTable.Columns.Count + 1}");
                            }
                        }
                        
                        DataRow row = dataTable.NewRow();
                        for (int i = 0; i < fields.Length; i++)
                        {
                            if (i < dataTable.Columns.Count)
                            {
                                row[i] = fields[i];
                            }
                        }
                        dataTable.Rows.Add(row);
                    }
                    
                    rowCount++;
                }
            }
            
            return dataTable;
        }

        /// <summary>
        /// 解析CSV行,处理逗号和引号
        /// </summary>
        private string[] ParseCsvLine(string line)
        {
            var fields = new System.Collections.Generic.List<string>();
            bool inQuotes = false;
            var currentField = new StringBuilder();
            
            for (int i = 0; i < line.Length; i++)
            {
                char c = line[i];
                
                if (c == '"')
                {
                    // 处理转义引号
                    if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
                    {
                        currentField.Append('"');
                        i++;
                    }
                    else
                    {
                        inQuotes = !inQuotes;
                    }
                }
                else if (c == ',' && !inQuotes)
                {
                    // 字段结束
                    fields.Add(currentField.ToString());
                    currentField.Clear();
                }
                else
                {
                    currentField.Append(c);
                }
            }
            
            // 添加最后一个字段
            fields.Add(currentField.ToString());
            
            return fields.ToArray();
        }

        /// <summary>
        /// 读取Excel文件并返回DataTable
        /// </summary>
        public DataTable ReadExcel(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException("文件不存在", filePath);
            
            IWorkbook workbook;
            DataTable dataTable = new DataTable();
            int rowCount = 0;
            
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                // 根据扩展名创建不同的工作簿
                if (Path.GetExtension(filePath).Equals(".xlsx", StringComparison.OrdinalIgnoreCase))
                {
                    workbook = new XSSFWorkbook(fileStream);
                }
                else if (Path.GetExtension(filePath).Equals(".xls", StringComparison.OrdinalIgnoreCase))
                {
                    workbook = new HSSFWorkbook(fileStream);
                }
                else
                {
                    throw new NotSupportedException("不支持的Excel文件格式");
                }
                
                // 获取第一个工作表
                ISheet sheet = workbook.GetSheetAt(0);
                if (sheet == null) return dataTable;
                
                // 获取标题行
                IRow headerRow = sheet.GetRow(0);
                if (headerRow == null) return dataTable;
                
                // 创建列
                for (int i = 0; i < headerRow.LastCellNum; i++)
                {
                    ICell cell = headerRow.GetCell(i);
                    string columnName = cell?.ToString() ?? $"Column{i + 1}";
                    dataTable.Columns.Add(columnName);
                }
                
                // 添加数据行
                for (int i = 1; i <= sheet.LastRowNum; i++)
                {
                    IRow row = sheet.GetRow(i);
                    if (row == null) continue;
                    
                    DataRow dataRow = dataTable.NewRow();
                    for (int j = 0; j < dataTable.Columns.Count; j++)
                    {
                        ICell cell = row.GetCell(j);
                        if (cell != null)
                        {
                            switch (cell.CellType)
                            {
                                case CellType.String:
                                    dataRow[j] = cell.StringCellValue;
                                    break;
                                case CellType.Numeric:
                                    if (DateUtil.IsCellDateFormatted(cell))
                                    {
                                        dataRow[j] = cell.DateCellValue.ToString("yyyy-MM-dd");
                                    }
                                    else
                                    {
                                        dataRow[j] = cell.NumericCellValue;
                                    }
                                    break;
                                case CellType.Boolean:
                                    dataRow[j] = cell.BooleanCellValue;
                                    break;
                                case CellType.Formula:
                                    dataRow[j] = cell.NumericCellValue;
                                    break;
                                default:
                                    dataRow[j] = cell.ToString();
                                    break;
                            }
                        }
                    }
                    dataTable.Rows.Add(dataRow);
                    rowCount++;
                }
            }
            
            return dataTable;
        }
    }
}

3. 数据处理类 (DataProcessor.cs)

csharp 复制代码
using System;
using System.Data;
using System.Globalization;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.HSSF.UserModel;

namespace CsvExcelReader
{
    public class DataProcessor
    {
        /// <summary>
        /// 处理DataTable数据(类型转换、空值处理等)
        /// </summary>
        public DataTable ProcessDataTable(DataTable inputTable)
        {
            if (inputTable == null) return null;
            
            DataTable processedTable = inputTable.Clone();
            
            // 处理列数据类型
            foreach (DataColumn column in processedTable.Columns)
            {
                // 尝试推断列的数据类型
                Type inferredType = InferColumnType(inputTable, column.ColumnName);
                if (inferredType != null)
                {
                    column.DataType = inferredType;
                }
            }
            
            // 处理行数据
            foreach (DataRow row in inputTable.Rows)
            {
                DataRow newRow = processedTable.NewRow();
                
                for (int i = 0; i < inputTable.Columns.Count; i++)
                {
                    string columnName = inputTable.Columns[i].ColumnName;
                    object value = row[i];
                    
                    // 处理空值
                    if (value == null || value == DBNull.Value || string.IsNullOrWhiteSpace(value.ToString()))
                    {
                        newRow[columnName] = DBNull.Value;
                        continue;
                    }
                    
                    // 处理数值类型
                    if (processedTable.Columns[columnName].DataType == typeof(int))
                    {
                        if (int.TryParse(value.ToString(), out int intValue))
                        {
                            newRow[columnName] = intValue;
                        }
                        else
                        {
                            newRow[columnName] = DBNull.Value;
                        }
                    }
                    else if (processedTable.Columns[columnName].DataType == typeof(double))
                    {
                        if (double.TryParse(value.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out double doubleValue))
                        {
                            newRow[columnName] = doubleValue;
                        }
                        else
                        {
                            newRow[columnName] = DBNull.Value;
                        }
                    }
                    else if (processedTable.Columns[columnName].DataType == typeof(DateTime))
                    {
                        if (DateTime.TryParse(value.ToString(), out DateTime dateValue))
                        {
                            newRow[columnName] = dateValue;
                        }
                        else
                        {
                            newRow[columnName] = DBNull.Value;
                        }
                    }
                    else
                    {
                        newRow[columnName] = value.ToString().Trim();
                    }
                }
                
                processedTable.Rows.Add(newRow);
            }
            
            return processedTable;
        }

        /// <summary>
        /// 推断列的数据类型
        /// </summary>
        private Type InferColumnType(DataTable table, string columnName)
        {
            int totalRows = table.Rows.Count;
            if (totalRows == 0) return typeof(string);
            
            int intCount = 0;
            int doubleCount = 0;
            int dateCount = 0;
            int stringCount = 0;
            
            foreach (DataRow row in table.Rows)
            {
                object value = row[columnName];
                if (value == null || value == DBNull.Value) continue;
                
                string strValue = value.ToString().Trim();
                if (string.IsNullOrEmpty(strValue)) continue;
                
                if (int.TryParse(strValue, out _))
                {
                    intCount++;
                }
                else if (double.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
                {
                    doubleCount++;
                }
                else if (DateTime.TryParse(strValue, out _))
                {
                    dateCount++;
                }
                else
                {
                    stringCount++;
                }
            }
            
            // 如果80%以上的值都是整数,则认为是整数类型
            if ((double)intCount / totalRows > 0.8) return typeof(int);
            
            // 如果80%以上的值都是浮点数,则认为是浮点数类型
            if ((double)(intCount + doubleCount) / totalRows > 0.8) return typeof(double);
            
            // 如果80%以上的值都是日期,则认为是日期类型
            if ((double)dateCount / totalRows > 0.8) return typeof(DateTime);
            
            return typeof(string);
        }

        /// <summary>
        /// 导出DataTable到CSV文件
        /// </summary>
        public void ExportToCsv(DataTable dataTable, string filePath)
        {
            if (dataTable == null) return;
            
            using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
            {
                // 写入列头
                for (int i = 0; i < dataTable.Columns.Count; i++)
                {
                    writer.Write(EscapeCsvField(dataTable.Columns[i].ColumnName));
                    if (i < dataTable.Columns.Count - 1)
                    {
                        writer.Write(",");
                    }
                }
                writer.WriteLine();
                
                // 写入数据行
                foreach (DataRow row in dataTable.Rows)
                {
                    for (int i = 0; i < dataTable.Columns.Count; i++)
                    {
                        object value = row[i];
                        string strValue = value?.ToString() ?? string.Empty;
                        writer.Write(EscapeCsvField(strValue));
                        if (i < dataTable.Columns.Count - 1)
                        {
                            writer.Write(",");
                        }
                    }
                    writer.WriteLine();
                }
            }
        }

        /// <summary>
        /// 转义CSV字段
        /// </summary>
        private string EscapeCsvField(string field)
        {
            if (string.IsNullOrEmpty(field)) return string.Empty;
            
            // 如果包含逗号、换行符或双引号,则用双引号包围
            if (field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r"))
            {
                return "\"" + field.Replace("\"", "\"\"") + "\"";
            }
            return field;
        }

        /// <summary>
        /// 导出DataTable到Excel文件
        /// </summary>
        public void ExportToExcel(DataTable dataTable, string filePath)
        {
            if (dataTable == null) return;
            
            IWorkbook workbook;
            if (Path.GetExtension(filePath).Equals(".xlsx", StringComparison.OrdinalIgnoreCase))
            {
                workbook = new XSSFWorkbook();
            }
            else
            {
                workbook = new HSSFWorkbook();
            }
            
            ISheet sheet = workbook.CreateSheet("Sheet1");
            
            // 创建标题行
            IRow headerRow = sheet.CreateRow(0);
            for (int i = 0; i < dataTable.Columns.Count; i++)
            {
                ICell cell = headerRow.CreateCell(i);
                cell.SetCellValue(dataTable.Columns[i].ColumnName);
                
                // 设置标题样式
                ICellStyle style = workbook.CreateCellStyle();
                IFont font = workbook.CreateFont();
                font.Boldweight = (short)FontBoldWeight.Bold;
                style.SetFont(font);
                cell.CellStyle = style;
            }
            
            // 填充数据
            for (int i = 0; i < dataTable.Rows.Count; i++)
            {
                IRow row = sheet.CreateRow(i + 1);
                for (int j = 0; j < dataTable.Columns.Count; j++)
                {
                    ICell cell = row.CreateCell(j);
                    object value = dataTable.Rows[i][j];
                    
                    if (value == DBNull.Value || value == null)
                    {
                        cell.SetCellValue(string.Empty);
                    }
                    else if (value is int intValue)
                    {
                        cell.SetCellValue(intValue);
                    }
                    else if (value is double doubleValue)
                    {
                        cell.SetCellValue(doubleValue);
                    }
                    else if (value is DateTime dateValue)
                    {
                        cell.SetCellValue(dateValue);
                        ICellStyle dateStyle = workbook.CreateCellStyle();
                        IDataFormat format = workbook.CreateDataFormat();
                        dateStyle.DataFormat = format.GetFormat("yyyy-mm-dd");
                        cell.CellStyle = dateStyle;
                    }
                    else
                    {
                        cell.SetCellValue(value.ToString());
                    }
                }
            }
            
            // 自动调整列宽
            for (int i = 0; i < dataTable.Columns.Count; i++)
            {
                sheet.AutoSizeColumn(i);
            }
            
            // 保存文件
            using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                workbook.Write(fileStream);
            }
        }
    }
}

4. 程序入口 (Program.cs)

csharp 复制代码
using System;
using System.Windows.Forms;

namespace CsvExcelReader
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}

5. 应用程序配置文件 (App.config)

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup> 
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="NPOI" publicKeyToken="0DF73EC7942D9264" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-2.5.6.0" newVersion="2.5.6.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="NPOI.OOXML" publicKeyToken="0DF73EC7942D9264" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-2.5.6.0" newVersion="2.5.6.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="NPOI.HPSF" publicKeyToken="0DF73EC7942D9264" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-2.5.6.0" newVersion="2.5.6.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

6. NuGet 包配置 (packages.config)

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="NPOI" version="2.5.6" targetFramework="net472" />
  <package id="NPOI.OOXML" version="2.5.6" targetFramework="net472" />
  <package id="NPOI.HPSF" version="2.5.6" targetFramework="net472" />
</packages>

功能特点

1. 文件读取功能

  • CSV文件

    • 支持逗号分隔的标准CSV格式
    • 自动处理引号包围的字段
    • 支持包含逗号的字段内容
    • 自动检测列头和创建数据结构
  • Excel文件

    • 支持.xls和.xlsx格式
    • 自动识别第一个工作表
    • 智能数据类型识别(文本、数值、日期)
    • 处理合并单元格和公式

2. 数据处理功能

  • 数据类型推断

    • 自动识别整数、浮点数、日期和文本
    • 基于数据分布智能判断最佳类型
  • 数据清洗

    • 自动去除多余空格
    • 处理空值和DBNull
    • 格式化日期和数字
  • 错误处理

    • 捕获并显示文件读取错误
    • 处理格式不正确的数据行
    • 提供详细的错误信息

3. 用户界面功能

  • 直观的操作界面

    • 清晰的按钮布局
    • 状态提示和进度指示
    • 响应式设计
  • 数据预览

    • 自动调整列宽
    • 交替行颜色提高可读性
    • 支持滚动查看大数据集
  • 数据导出

    • 一键导出为CSV或Excel
    • 保留数据类型和格式
    • 自动命名导出文件

4. 性能优化

  • 流式读取

    • 大文件分块处理
    • 内存高效的数据加载
  • 后台处理

    • 进度条显示加载状态
    • 异步操作防止界面冻结
  • 资源管理

    • 及时释放文件句柄
    • 使用using语句确保资源释放

使用说明

1. 环境要求

  • .NET Framework 4.7.2 或更高版本

  • 安装NuGet包:

    复制代码
    Install-Package NPOI
    Install-Package NPOI.OOXML
    Install-Package NPOI.HPSF

2. 操作步骤

  1. 打开文件

    • 点击"打开CSV文件"按钮选择CSV文件
    • 点击"打开Excel文件"按钮选择Excel文件
  2. 查看数据

    • 文件加载后显示在DataGridView中
    • 自动识别数据类型并显示
    • 可通过滚动条查看所有数据
  3. 导出数据

    • 加载数据后导出按钮启用
    • 点击"保存为CSV"导出为CSV格式
    • 点击"保存为Excel"导出为Excel格式
  4. 清除数据

    • 点击"清除数据"按钮清空当前显示

3. 使用示例

csharp 复制代码
// 示例:在代码中直接使用读取器
var reader = new FileReader();

// 读取CSV文件
DataTable csvData = reader.ReadCsv(@"C:\data\sales.csv");

// 读取Excel文件
DataTable excelData = reader.ReadExcel(@"C:\data\inventory.xlsx");

// 处理数据
var processor = new DataProcessor();
DataTable processedData = processor.ProcessDataTable(csvData);

// 导出数据
processor.ExportToCsv(processedData, @"C:\output\processed.csv");
processor.ExportToExcel(processedData, @"C:\output\processed.xlsx");

参考代码 C# 读取csv/excel文件数据至datagridview(ExcelDataReader) www.youwenfan.com/contentcst/45118.html

技术亮点

1. CSV解析算法

csharp 复制代码
private string[] ParseCsvLine(string line)
{
    var fields = new List<string>();
    bool inQuotes = false;
    var currentField = new StringBuilder();
    
    for (int i = 0; i < line.Length; i++)
    {
        char c = line[i];
        
        if (c == '"')
        {
            // 处理转义引号
            if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
            {
                currentField.Append('"');
                i++;
            }
            else
            {
                inQuotes = !inQuotes;
            }
        }
        else if (c == ',' && !inQuotes)
        {
            // 字段结束
            fields.Add(currentField.ToString());
            currentField.Clear();
        }
        else
        {
            currentField.Append(c);
        }
    }
    
    // 添加最后一个字段
    fields.Add(currentField.ToString());
    
    return fields.ToArray();
}

2. Excel数据类型处理

csharp 复制代码
switch (cell.CellType)
{
    case CellType.String:
        dataRow[j] = cell.StringCellValue;
        break;
    case CellType.Numeric:
        if (DateUtil.IsCellDateFormatted(cell))
        {
            dataRow[j] = cell.DateCellValue.ToString("yyyy-MM-dd");
        }
        else
        {
            dataRow[j] = cell.NumericCellValue;
        }
        break;
    case CellType.Boolean:
        dataRow[j] = cell.BooleanCellValue;
        break;
    case CellType.Formula:
        dataRow[j] = cell.NumericCellValue;
        break;
    default:
        dataRow[j] = cell.ToString();
        break;
}

3. 数据类型推断

csharp 复制代码
private Type InferColumnType(DataTable table, string columnName)
{
    int totalRows = table.Rows.Count;
    if (totalRows == 0) return typeof(string);
    
    int intCount = 0;
    int doubleCount = 0;
    int dateCount = 0;
    int stringCount = 0;
    
    foreach (DataRow row in table.Rows)
    {
        // 分析每个值的类型...
    }
    
    // 根据分布确定最佳类型
    if ((double)intCount / totalRows > 0.8) return typeof(int);
    if ((double)(intCount + doubleCount) / totalRows > 0.8) return typeof(double);
    if ((double)dateCount / totalRows > 0.8) return typeof(DateTime);
    
    return typeof(string);
}

扩展功能

1. 添加数据库支持

csharp 复制代码
public class DatabaseExporter
{
    public void ExportToSqlServer(DataTable data, string connectionString, string tableName)
    {
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            connection.Open();
            using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
            {
                bulkCopy.DestinationTableName = tableName;
                
                // 映射列
                foreach (DataColumn column in data.Columns)
                {
                    bulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }
                
                bulkCopy.WriteToServer(data);
            }
        }
    }
}

2. 添加数据过滤功能

csharp 复制代码
public DataTable FilterData(DataTable data, string columnName, string filterValue)
{
    DataView dv = new DataView(data);
    dv.RowFilter = $"[{columnName}] LIKE '%{filterValue}%'";
    return dv.ToTable();
}

3. 添加数据统计功能

csharp 复制代码
public DataTable GetSummaryStatistics(DataTable data)
{
    DataTable summary = new DataTable();
    summary.Columns.Add("Column", typeof(string));
    summary.Columns.Add("Type", typeof(string));
    summary.Columns.Add("Count", typeof(int));
    summary.Columns.Add("NullCount", typeof(int));
    summary.Columns.Add("Min", typeof(string));
    summary.Columns.Add("Max", typeof(string));
    summary.Columns.Add("Avg", typeof(string));
    
    foreach (DataColumn column in data.Columns)
    {
        DataRow row = summary.NewRow();
        row["Column"] = column.ColumnName;
        row["Type"] = column.DataType.Name;
        
        int count = data.Rows.Count;
        int nullCount = 0;
        double? min = null, max = null, sum = 0;
        int numericCount = 0;
        
        foreach (DataRow dataRow in data.Rows)
        {
            object value = dataRow[column];
            if (value == DBNull.Value || value == null)
            {
                nullCount++;
                continue;
            }
            
            if (column.DataType == typeof(int) || column.DataType == typeof(double))
            {
                double numValue = Convert.ToDouble(value);
                if (!min.HasValue || numValue < min) min = numValue;
                if (!max.HasValue || numValue > max) max = numValue;
                sum += numValue;
                numericCount++;
            }
        }
        
        row["Count"] = count;
        row["NullCount"] = nullCount;
        row["Min"] = min?.ToString() ?? "N/A";
        row["Max"] = max?.ToString() ?? "N/A";
        row["Avg"] = numericCount > 0 ? (sum / numericCount).ToString() : "N/A";
        
        summary.Rows.Add(row);
    }
    
    return summary;
}

常见问题解决

1. 文件格式问题

  • 问题:中文乱码

  • 解决:在读取CSV时指定正确的编码

    csharp 复制代码
    // 尝试不同编码
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
    using (var reader = new StreamReader(filePath, Encoding.GetEncoding("GB2312")))

2. 大文件处理

  • 问题:加载大文件时内存不足

  • 解决:使用分页加载

    csharp 复制代码
    // 分批读取大文件
    public DataTable ReadLargeCsv(string filePath, int batchSize = 1000)
    {
        // 实现分页读取逻辑
    }

3. 特殊格式处理

  • 问题:Excel中的公式值

  • 解决:使用NPOI的公式求值器

    csharp 复制代码
    IFormulaEvaluator evaluator = workbook.GetCreationHelper().CreateFormulaEvaluator();
    CellValue cellValue = evaluator.Evaluate(cell);

项目总结

这个C#解决方案提供了完整的CSV/Excel文件读取和显示功能,具有以下特点:

  1. 多格式支持

    • 支持CSV和Excel文件(.xls/.xlsx)
    • 智能识别文件格式
    • 处理各种数据格式和编码
  2. 强大的数据处理

    • 自动数据类型推断
    • 数据清洗和转换
    • 空值处理和错误恢复
  3. 用户友好界面

    • 直观的操作流程
    • 实时状态反馈
    • 响应式数据展示
  4. 灵活的导出功能

    • 支持CSV和Excel导出
    • 保留数据类型和格式
    • 自定义导出选项
  5. 健壮的错误处理

    • 全面的异常捕获
    • 详细的错误信息
    • 优雅的降级处理
相关推荐
傻啦嘿哟2 小时前
如何用 Python 拆分 Word 文件:高效分割大型文档的完整指南
开发语言·c#
高斯林.神犇2 小时前
五、注解方式管理bean
java·开发语言
xiaotao1312 小时前
01-编程基础与数学基石: 常用内置库
开发语言·人工智能·python
xiaoshuaishuai82 小时前
C# 实现“superpowers进化
运维·服务器·windows·c#
神仙别闹2 小时前
基于 MATLAB 实现 Word 的信息隐藏算法
c#·word·xhtml
一只大袋鼠3 小时前
MySQL 进阶:聚集函数、分组、约束、多表查询
开发语言·数据库·mysql
csdn_aspnet11 小时前
C# (QuickSort using Random Pivoting)使用随机枢轴的快速排序
数据结构·算法·c#·排序算法
以神为界11 小时前
Python入门实操:基础语法+爬虫入门+模块使用全指南
开发语言·网络·爬虫·python·安全·web
逻辑驱动的ken12 小时前
Java高频面试题:03
java·开发语言·面试·求职招聘·春招