繁体   English   中英

在 DataGridView 中读取 CSV 文件

[英]Read CSV file in DataGridView

我想将 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);
}

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

使用一点 linq 的第一个解决方案

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 中添加列。

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

Obs.: 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 的作者在库中构建功能。 代码变得简单:

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 文件没有“First-Line-Column-Header”,我个人出于某些原因没有将它们放在那里,所以这是文件示例

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

所以你的想法对吗?

现在,我将手动将Columns添加到DataTable 而且我将使用Tasks异步执行此操作。 并且只需使用foreach循环使用以下函数将值添加到DataTable.Rows中:

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;

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM