运行代码读取csv文件就会出现列名重复的问题,将列取消了可以一行一行读出数据,但是datatable里面没有保存数据,输出之后是个空csv文件。我的CSV文件是由一堆纯数据组成的,会有重复的数据出现,因此会出现名为xx的列已属于此datatable这个问题该怎么解决
using System;
using System.Data;
using System.IO;
using System.Text;
namespace WindowsFormsApp3
{
public class CSVHelper
{
/// <summary>
/// 读取CSV文件
/// </summary>
/// <param name="fileName">文件路径</param>
public static DataTable ReadCSV(string fileName)
{
DataTable dt = new DataTable();
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs, Encoding.UTF8);
//分隔符
string[] separators = { "," };
//记录每次读取的一行记录
string strLine;
//逐行读取CSV文件
while ((strLine = sr.ReadLine()) != null)
{
strLine = strLine.Trim();//去除头尾空格
//记录每行记录中的各字段内容
string[] arrayLine = strLine.Split(separators, StringSplitOptions.RemoveEmptyEntries);
//列的个数
int dtColumns = arrayLine.Length;
for (int i = 0; i < dtColumns; i++)
{
dt.Columns.Add(arrayLine[i]);//每一列名称
}
DataRow dataRow = dt.NewRow();//新建一行
dt.TableName = "CsvData";
dt.Rows.Add(dataRow);//添加一行
}
DataSet ds = new DataSet();
if (ds.Tables.Count > 0)
{
Console.WriteLine("有"); //有数据
}
else if((ds.Tables.Count == 1 && ds.Tables[0].Rows.Count == 0) || ds == null || ds.Tables.Count == 0)
{
Console.WriteLine("无"); //无数据
}
sr.Close();
fs.Close();
return dt;
}
/// <summary>
/// 写入CSV
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="dt">要写入的datatable</param>
public static void WriteCSV(string fileName, DataTable dt)
{
FileStream fs;
StreamWriter sw;
string data = null;
//判断文件是否存在,存在就不再次写入列名
if (!File.Exists(fileName))
{
fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
sw = new StreamWriter(fs, Encoding.UTF8);
//写出列名称
for (int i = 0; i < dt.Columns.Count; i++)
{
data += dt.Columns[i].ColumnName.ToString();
if (i < dt.Columns.Count - 1)
{
data += ",";//中间用,隔开
}
}
sw.WriteLine(data);
}
else
{
fs = new FileStream(fileName, FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs, Encoding.UTF8);
}
//写出各行数据
for (int i = 0; i < dt.Rows.Count; i++)
{
data = null;
for (int j = 0; j < dt.Columns.Count; j++)
{
data += dt.Rows[i][j].ToString();
if (j < dt.Columns.Count - 1)
{
data += ",";//中间用,隔开
}
}
}
sw.Close();
fs.Close();
}
}
}
直接调用下面准备个方法就好了
/// <summary>
/// 导出CSV文件并打开
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dcCsv_Click(object sender, EventArgs e)
{
string dir = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName + "\\db\\csv\\";
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
//获取文件地址
string dicpath = dir + DateTime.Now.Ticks + ".csv";
System.Diagnostics.Process.Start("Explorer.exe", dicpath);
}
/// <summary>
/// datatable保存为csv文件
/// </summary>
/// <param name="dt"></param>
/// <param name="filePath"></param>
public static void TableToCsv(DataTable dt, string filePath)
{
StringBuilder sb = new StringBuilder();
List<string> colname = new List<string>();
for (int j = 0; j < dt.Columns.Count; j++)
{
colname.Add(val01(dt.Columns[j].ColumnName));
}
sb.Append(string.Join(",", colname.ToArray()));
sb.Append("\n");
for (int i = 0; i < dt.Rows.Count; i++)
{
List<string> rowval = new List<string>();
for (int j = 0; j < dt.Columns.Count; j++)
{
rowval.Add(val01(dt.Rows[i][j].ToString()));
}
sb.Append(string.Join(",", rowval.ToArray()));
sb.Append("\n");
}
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
#endregion
你读取csv的循环有问题。对一个表格来说,只会有一个列头行(或者说列名行);在csv里,第一行就是列头。
你贴出来的代码中循环第一次执行的时候,读取的是csv第一行的内容,也就是列头,这时候你把第一行的内容用","分隔符拆开,作为每一列的列头,是可以的。但之后读每一行的数据的时候,就不应该再解析为列头了。因为你把数据作为列头加到你dt的列头里了,当下次碰到相同的数据,要再次加列头的时候,dt就报错了:你前面已经加过这个名字的列头了。
简单改一下,加上行号,每次循环判断一下:
如果是第一行,那就解析为列头,加到你定义的dt里;处理完列头后,行号++,下次循环就不用++了,因为这个行号只是为了判断是不是列头行。(一样,也可以用bool来做这个事情)
如果不是第一行,那就是数据,new 一个row出来,同样,内容用","分隔符拆开,拆开的数据作为当前行每一列的数据值存入这个row。当前行解析完后,再把当前row添加到dt中。————这个我没给你实现。
纯手打,望采纳
//记录每次读取的一行记录
string strLine;
//————增,行号
int i =0;
//逐行读取CSV文件
while ((strLine = sr.ReadLine()) != null)
{
strLine = strLine.Trim();//去除头尾空格
//记录每行记录中的各字段内容
string[] arrayLine = strLine.Split(separators, StringSplitOptions.RemoveEmptyEntries);
//————判断是不是第一行。第一行:列头行。非第一行:数据行
if(i==0)
{
//列的个数
int dtColumns = arrayLine.Length;
for (int i = 0; i < dtColumns; i++)
{
dt.Columns.Add(arrayLine[i]);//每一列名称
}
//————i自增一下,之后每次while到这个if,都不会执行当前这一段了
i++;
}
else
{
DataRow dataRow = dt.NewRow();//新建一行
//下面这句出现在这里虽然不会报错,但没人这么干。给表格起名不需要重复很多次起同一个名字。
//dt.TableName = "CsvData";
//————在这里要把当前行的数据解析出来放到上面的这个dataRow里,我就不给你实现了。
//————dataRow里添加完当前行数据后,再把这个有数据的row放到dt的rows里
dt.Rows.Add(dataRow);//添加一行
}
}
可以参考我这个例子,是可以的
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace csvDemo
{
internal class Program
{
static void Main(string[] args)
{
// WriteCsv("1,谭阳,21");
List<string> datas;
ReadCsv("D:\\CSV\\2021-12-22-11.csv", out datas);//读取csv
Console.WriteLine(datas[1].Split(',')[1]);//前方数字为对应的行(1为去掉表头行的第一行),后方的1为对应的列(为第二列)
Console.WriteLine(datas[1].Split(',')[2]);
Console.WriteLine(datas[1].Split(',')[3]);
Console.ReadLine();
}
/// <summary>
/// 写入csv
/// </summary>
/// <param name="result">写入内容 ----单元格内容,单元格内容-----</param>
public static void WriteCsv(string result)
{
string path = "D:\\CSV\\";//保存路径
string fileName = path + DateTime.Now.ToString("yyyy-MM-dd-HH") + ".csv";//文件名
string Datedate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");//年月日小时分钟秒
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
if (!File.Exists(fileName))
{
StreamWriter sw = new StreamWriter(fileName, true, Encoding.UTF8);
string str1 = "时间"+","+"编号" + "," + "姓名" + "," + "年龄" + "\t\n";
sw.Write(str1);
sw.Close();
}
StreamWriter swl = new StreamWriter(fileName, true, Encoding.UTF8);
string str = Datedate + "," + result + "\t\n";
swl.Write(str);
swl.Close();
}
//读取csv
public static void ReadCsv(string path, out List<string> data)
{
StreamReader sr;
data = new List<string>();
try
{
using (sr = new StreamReader(path, Encoding.GetEncoding("GB2312")))
{
string str = "";
while ((str = sr.ReadLine()) != null)
{
data.Add(str);
}
}
}
catch (Exception ex)
{
foreach (Process process in Process.GetProcesses())
{
if (process.ProcessName.ToUpper().Equals("EXCEL"))
process.Kill();
}
GC.Collect();
Thread.Sleep(10);
Console.WriteLine(ex.StackTrace);
using (sr = new StreamReader(path, Encoding.GetEncoding("GB2312")))
{
string str = "";
while ((str = sr.ReadLine()) != null)
{
data.Add(str);
}
}
}
}
}
}
你要把csv的数据长度计算出来,然后读取的时候要严格按照数据长度来处理,超出了就会重头开始读取数据。我用codsys读的时候也发现了重复的问题,改了数据长度就好了,这个博客可以参考下,https://blog.csdn.net/qq_19979629/article/details/124309153
C#读取CSV,首先需了解这个类型文件的行列分隔符,按分隔符截取即可
https://wenku.baidu.com/view/a104e17831b765ce05081413.html
//实例化一个datatable用来存储数据
DataTable dt = new DataTable();
//文件流读取
System.IO.FileStream fs = new System.IO.FileStream("d:\\1.csv", System.IO.FileMode.Open);
System.IO.StreamReader sr = new System.IO.StreamReader(fs, Encoding.GetEncoding("gb2312"));
string tempText = "";
bool isFirst = true;
while ((tempText = sr.ReadLine()) != null)
{
string[] arr = tempText.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
//一般第一行为标题,所以取出来作为标头
if (isFirst)
{
foreach (string str in arr)
{
dt.Columns.Add(str);
}
isFirst = false;
}
else
{
//从第二行开始添加到datatable数据行
DataRow dr = dt.NewRow();
for (int i = 0; i < dt.Columns.Count; i++)
{
dr[i] = i < arr.Length ? arr[i] : "";
}
dt.Rows.Add(dr);
}
}
//展示到页面
dataGridView1.DataSource = dt;
//关闭流
sr.Close(); fs.Close();
直接调用这个方法