如何用C#读写输出CSV文件?

运行代码读取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();

直接调用这个方法