如何用C#在CSV数据表顶部插入空行并适配DataGridView?
问题描述
我用C#开发Windows Forms桌面应用,将上传的CSV数据存入DataGridView后,转置功能无法正常工作——索引为0的行未被包含在转置输出文件里。手动在CSV顶部插入空行后,应用显示和转置功能都恢复正常,我试过用以下代码插入空行,但没达到预期效果:
DataRow dr1 = dt.NewRow(); dt.Rows.InsertAt(dr1, 0);
需要写出能在索引0位置插入空行并在DataGridView正常显示,让转置功能恢复正常的代码。
存储数据到DataGridView的代码
public DataSet ConnectCSV() { DataSet ds = new DataSet(); string fileName = openFileDialogCSVFilePath.FileName; CsvReader reader = new CsvReader(fileName); ds = reader.RowEnumerator; dGridCSVdata.DataSource = ds; dGridCSVdata.DataMember = "TheData"; return ds; } private void dGridCSVdata_Navigate(object sender, NavigateEventArgs ne) { }
CSV读取与转置的代码
public DataSet RowEnumerator { get { if (null == __reader) throw new System.ApplicationException("I can't start reading without CSV input."); __rowno = 0; string sLine; string sNextLine; DataSet ds = new DataSet(); DataTable dt = ds.Tables.Add("TheData"); DataRow dr1 = dt.NewRow(); dt.Rows.InsertAt(dr1, 0); while (null != (sLine = __reader.ReadLine())) { while (rexRunOnLine.IsMatch(sLine) && null != (sNextLine = __reader.ReadLine())) sLine += "\n" + sNextLine; __rowno++; DataRow dr = dt.NewRow(); string[] values = rexCsvSplitter.Split(sLine); for (int i = 0; i < values.Length; i++) { values[i] = Csv.Unescape(values[i]); if (__rowno == 1) { dt.Columns.Add(values[i].Trim()); } else { if (Csv.CharNotAllowes(values[i])) { dr[i] = values[i].Trim(); } } } if (__rowno != 1) { dt.Rows.Add(dr); } ds.Tables.Add(Transposer.Transpose(dt)); // transpose code StringBuilder sb = new StringBuilder(); for (int u = 0; u < dt.Columns.Count; u++) { for (int i = 0; i < dt.Rows.Count; i++) { sb.Append(dt.Rows[i][u].ToString()); if (i < dt.Rows.Count - 1) { sb.Append(','); } } sb.AppendLine(); } File.WriteAllText("C:\\Users\\Desktop\\Output.csv", sb.ToString()); } __reader.Close(); return ds; } }
解决方案
问题出在你插入空行的时机不对——一开始就插入空行时,DataTable还没有创建任何列,空行没有对应的数据结构,导致后续转置逻辑异常。同时你每读取一行就写入一次输出文件,会造成文件被多次覆盖。
修改后的RowEnumerator代码如下:
public DataSet RowEnumerator { get { if (null == __reader) throw new System.ApplicationException("I can't start reading without CSV input."); __rowno = 0; string sLine; string sNextLine; DataSet ds = new DataSet(); DataTable dt = ds.Tables.Add("TheData"); while (null != (sLine = __reader.ReadLine())) { while (rexRunOnLine.IsMatch(sLine) && null != (sNextLine = __reader.ReadLine())) sLine += "\n" + sNextLine; __rowno++; string[] values = rexCsvSplitter.Split(sLine); values = Array.ConvertAll(values, v => Csv.Unescape(v).Trim()); if (__rowno == 1) { // 先读取表头,创建DataTable的所有列 foreach (string colName in values) { dt.Columns.Add(colName); } // 列结构创建完成后,插入空行到索引0位置 DataRow emptyRow = dt.NewRow(); dt.Rows.InsertAt(emptyRow, 0); } else { DataRow dr = dt.NewRow(); for (int i = 0; i < values.Length; i++) { // 防止行数据列数与表头列数不匹配导致索引越界 if (i < dt.Columns.Count) { dr[i] = values[i]; } } dt.Rows.Add(dr); } } // 所有数据读取完成后,再执行转置和文件写入 ds.Tables.Add(Transposer.Transpose(dt)); StringBuilder sb = new StringBuilder(); for (int u = 0; u < dt.Columns.Count; u++) { for (int i = 0; i < dt.Rows.Count; i++) { sb.Append(dt.Rows[i][u].ToString()); if (i < dt.Rows.Count - 1) { sb.Append(','); } } sb.AppendLine(); } File.WriteAllText("C:\\Users\\Desktop\\Output.csv", sb.ToString()); __reader.Close(); return ds; } }
关键修改点
- 调整空行插入时机:先读取CSV第一行作为表头,创建完整的DataTable列结构,再插入空行,确保空行能匹配所有列。
- 优化文件写入逻辑:把转置和写入文件的代码移到循环外,避免每读一行就覆盖一次输出文件,确保所有数据加载完成后生成完整的转置结果。
- 增加列数校验:添加
if (i < dt.Columns.Count)判断,防止CSV行数据列数与表头列数不一致时出现索引越界错误。
内容的提问来源于stack exchange,提问作者nadyaggy
相关产品推荐
相关产品推荐

