读取DataGridView中的CSV文件

本文关键字:文件 CSV 中的 DataGridView 读取 | 更新日期: 2023-09-27 18:29:04

我想将csv文件读取到Datagridview中。我想要一个类和一个函数,它可以像这样读取csv:

class Import
{
    public DataTable readCSV(string filePath)
    {            
        DataTable dt = new DataTable();
        using (StreamReader sr = new StreamReader(filePath))
        {
            string strLine = sr.ReadLine();     
            string[] strArray = strLine.Split(';');
            foreach (string value in strArray)
            {
                dt.Columns.Add(value.Trim());
            } 
            DataRow dr = dt.NewRow();
            while (sr.Peek() >= 0)
            {
                strLine = sr.ReadLine();
                strArray = strLine.Split(';');
                dt.Rows.Add(strArray);
            }
        }
        return dt;
     }   
}

并称之为

Import imp = new Import();
DataTable table = imp.readCSV(filePath);
foreach(DataRow row in table.Rows)
{
 dataGridView.Rows.Add(row);
}

结果是->创建了行,但单元格中没有数据!!

读取DataGridView中的CSV文件

第一个使用linq的litle位的解决方案

public DataTable readCSV(string filePath)
{
    var dt = new DataTable();
    // Creating the columns
    File.ReadLines(filePath).Take(1)
        .SelectMany(x => x.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
        .ToList()
        .ForEach(x => dt.Columns.Add(x.Trim()));
    // Adding the rows
    File.ReadLines(filePath).Skip(1)
        .Select(x => x.Split(';'))
        .ToList()
        .ForEach(line => dt.Rows.Add(line));
    return dt;
}

下面是使用foreach循环的另一个版本

public DataTable readCSV(string filePath)
{
    var dt = new DataTable();
    // Creating the columns
    foreach(var headerLine in File.ReadLines(filePath).Take(1))
    {
        foreach(var headerItem in headerLine.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
        {
            dt.Columns.Add(headerItem.Trim());
        }
    }
    // Adding the rows
    foreach(var line in File.ReadLines(filePath).Skip(1))
    {
        dt.Rows.Add(x.Split(';'));
    }
    return dt;
}

首先,我们使用File.ReadLines,它返回一个作为行集合的IEnumerable。我们使用Take(1),只获取第一行,这应该是标题,然后我们使用SelectMany,它将在单个列表中转换从Split方法返回的字符串数组,所以我们调用ToList,现在我们可以使用ForEach方法在DataTable中添加Columns。

要添加行,我们仍然使用File.ReadLines,但现在我们跳过(1),这跳过了头行,现在我们将使用Select来创建Collection<Collection<string>>,然后再次调用ToList,最后调用ForEach来添加DataTable中的行。File.ReadLines在.NET 4.0中可用。

观察:File.ReadLines不会读取所有行,它返回一个IEnumerable,并且行是延迟求值的,所以只加载第一行两次。

请参阅MSDN备注

ReadLines和ReadAllLines方法的区别如下:使用ReadLines时,可以在返回整个集合之前开始枚举字符串集合;使用ReadAllLines时,必须等待返回整个字符串数组,然后才能访问该数组。因此,当您使用非常大的文件时,ReadLines可以更高效。

您可以使用ReadLines方法执行以下操作:

对文件执行LINQ to Objects查询,以获得已筛选的一组行。

使用file.WriteAllLines(String,IEnumerable)方法,或者使用file.AppendAllLines(String,IEnumerable)方法。

创建一个集合的立即填充实例,该实例采用IEnumerable字符串集合作为其构造函数,例如IList或Queue。

此方法使用UTF8作为编码值。

如果你还有任何疑问,看看这个答案:File.ReadLines()和File.ReadAllLines(()之间有什么区别?

使用CsvHelper包的第二个解决方案

首先,安装这个nuget包

PM> Install-Package CsvHelper 

对于给定的CSV,我们应该创建一个类来表示它

CSV文件

Name;Age;Birthdate;Working
Alberto Monteiro;25;01/01/1990;true
Other Person;5;01/01/2010;false

类模型为

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public DateTime Birthdate { get; set; }
    public bool Working { get; set; }
}

现在让我们使用CsvReader来构建DataTable

public DataTable readCSV(string filePath)
{
    var dt = new DataTable();
    var csv = new CsvReader(new StreamReader(filePath));
    // Creating the columns
    typeof(Person).GetProperties().Select(p => p.Name).ToList().ForEach(x => dt.Columns.Add(x));
    // Adding the rows
    csv.GetRecords<Person>().ToList.ForEach(line => dt.Rows.Add(line.Name, line.Age, line.Birthdate, line.Working));
    return dt;
}

要在DataTable e中创建列,请使用一点反射,然后使用GetRecords方法在DataTabble 中添加行

using Microsoft.VisualBasic.FileIO;

我建议如下。它至少应该有这样的优势:";"中的字段将被正确处理,并且它不受特定csv格式的限制。

public class CsvImport
{
    public static DataTable NewDataTable(string fileName, string delimiters, bool firstRowContainsFieldNames = true)
    {
        DataTable result = new DataTable();
        using (TextFieldParser tfp = new TextFieldParser(fileName))
        {
            tfp.SetDelimiters(delimiters); 
            // Get Some Column Names
            if (!tfp.EndOfData)
            {
                string[] fields = tfp.ReadFields();
                for (int i = 0; i < fields.Count(); i++)
                {
                    if (firstRowContainsFieldNames)
                        result.Columns.Add(fields[i]);
                    else 
                        result.Columns.Add("Col" + i);
                }
                // If first line is data then add it
                if (!firstRowContainsFieldNames)
                    result.Rows.Add(fields); 
            }
            // Get Remaining Rows
            while (!tfp.EndOfData) 
                result.Rows.Add(tfp.ReadFields());
        }
        return result;
    } 
}

CsvHelper的Author在库中构建功能。代码变得简单:

using (var reader = new StreamReader("path''to''file.csv"))
using (var csv = new CsvReader(reader, CultureInfo.CurrentCulture))
{
    // Do any configuration to `CsvReader` before creating CsvDataReader.
    using (var dr = new CsvDataReader(csv))
    {        
        var dt = new DataTable();
        dt.Load(dr);
    }
}

CultureInfo.CurrentCulture用于确定默认分隔符,如果您想读取Excel保存的csv,则需要该分隔符。

我也遇到了同样的问题,但我找到了一种方法,用我自己的方式使用@Alberto Monteiro的答案。。。

我的CSV文件没有";"第一行列标题";,由于某些原因,我个人没有把它们放在那里,所以这是的文件样本

1,john doe,j.doe,john.doe@company.net
2,jane doe,j.doe,jane.doe@company.net

所以你的想法是对的?

现在,我将手动将Columns添加到DataTable中。我还将使用Tasks异步地执行此操作。只需简单地使用CCD_ 6循环,使用以下函数将值添加到CCD_

public Task<DataTable> ImportFromCSVFileAsync(string filePath)
{
    return Task.Run(() =>
    {
        DataTable dt = new DataTable();
        dt.Columns.Add("Index");
        dt.Columns.Add("Full Name");
        dt.Columns.Add("User Name");
        dt.Columns.Add("Email Address");
        // splitting the values using Split() command 
        foreach(var srLine in File.ReadAllLines(filePath))
        {
            dt.Rows.Add(srLine.Split(','));
        }
        return dt;
    });
}

现在要调用函数,我只需ButtonClick即可完成的工作


private async void ImportToGrid_STRBTN_Click(object sender, EventArgs e)
{
   // Handling UI objects
   // Best idea for me was to put everything a Panel and Disable it while waiting
   // and after the job is done Enabling it
   // and using a toolstrip docked to bottom outside of the panel to show progress using a 
   // progressBar and setting its style to Marquee
   panel1.Enabled = false;
   progressbar1.Visible = true;
   try
   {
      DataTable dt = await ImportFromCSVFileAsync(@"c:'myfile.txt");
      if (dt.Rows.Count > 0)
      {
         Datagridview1.DataSource = null; // To clear the previous data before adding the new ones
         Datagridview1.DataSource = dt;
      }
   }
   catch (Exception ex)
   {
      MessagBox.Show(ex.Message, "Error");
   }
   progressbar1.Visible = false;
   panel1.Enabled = true;
}