简体   繁体   English

在 DataGridView 中读取 CSV 文件

[英]Read CSV file in DataGridView

I want to read a csv-file into a Datagridview.我想将 csv 文件读入 Datagridview。 I would like to have a class and a function which reads the csv like this one:我想要一个类和一个函数,它可以像这样读取 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;
     }   
}

and call it:并称之为:

Import imp = new Import();

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

Result of this is-> rows are created but there is no data in the cells !!结果是-> 创建了行,但单元格中没有数据

First solution using a litle bit of linq使用一点 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;
}

Below another version using foreach loop在使用 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;
}

First we use the File.ReadLines, that returns an IEnumerable that is a colletion of lines.首先,我们使用 File.ReadLines,它返回一个 IEnumerable,它是行的集合。 We use Take(1), to get just the first row, that should be the header, and then we use SelectMany that will transform the array of string returned from the Split method in a single list, so we call ToList and we can now use ForEach method to add Columns in DataTable.我们使用 Take(1) 来获取第一行,这应该是标题,然后我们使用 SelectMany 它将从 Split 方法返回的字符串数组转换为单个列表,因此我们调用 ToList,我们现在可以使用 ForEach 方法在 DataTable 中添加列。

To add the rows, we still use File.ReadLines, but now we Skip(1), this skip the header line, now we are going to use Select, to create a Collection<Collection<string>> , then again call ToList, and finally call ForEach to add the row in DataTable.要添加行,我们仍然使用 File.ReadLines,但现在我们 Skip(1),这会跳过标题行,现在我们将使用 Select,创建一个Collection<Collection<string>> ,然后再次调用 ToList,最后调用 ForEach 在 DataTable 中添加行。 File.ReadLines is available in .NET 4.0. File.ReadLines 在 .NET 4.0 中可用。

Obs.: File.ReadLines doesn't read all lines, it returns a IEnumerable, and lines are lazy evaluated, so just the first line will be loaded two times. Obs.: File.ReadLines不会读取所有行,它返回一个 IEnumerable,并且行是惰性评估的,因此只会加载第一行两次。

See the MSDN remarks见 MSDN 备注

The ReadLines and ReadAllLines methods differ as follows: When you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned; ReadLines 和 ReadAllLines 方法的区别如下: 使用 ReadLines 时,可以在返回整个集合之前开始枚举字符串集合; when you use ReadAllLines, you must wait for the whole array of strings be returned before you can access the array.使用 ReadAllLines 时,必须等待返回整个字符串数组才能访问该数组。 Therefore, when you are working with very large files, ReadLines can be more efficient.因此,当您处理非常大的文件时,ReadLines 会更有效率。

You can use the ReadLines method to do the following:您可以使用 ReadLines 方法执行以下操作:

Perform LINQ to Objects queries on a file to obtain a filtered set of its lines.对文件执行 LINQ to Objects 查询以获取过滤后的行集。

Write the returned collection of lines to a file with the File.WriteAllLines(String, IEnumerable) method, or append them to an existing file with the File.AppendAllLines(String, IEnumerable) method.使用 File.WriteAllLines(String, IEnumerable) 方法将返回的行集合写入文件,或使用 File.AppendAllLines(String, IEnumerable) 方法将它们附加到现有文件。

Create an immediately populated instance of a collection that takes an IEnumerable collection of strings for its constructor, such as a IList or a Queue.创建一个立即填充的集合实例,该实例采用 IEnumerable 字符串集合作为其构造函数,例如 IList 或 Queue。

This method uses UTF8 for the encoding value.此方法使用 UTF8 作为编码值。

If you still have any doubt look this answer: What is the difference between File.ReadLines() and File.ReadAllLines()?如果您仍有任何疑问,请查看以下答案: File.ReadLines() 和 File.ReadAllLines() 有什么区别?

Second solution using CsvHelper package使用 CsvHelper 包的第二种解决方案

First, install this nuget package首先,安装这个nuget包

PM> Install-Package CsvHelper 

For a given CSV, we should create a class to represent it对于给定的 CSV,我们应该创建一个类来表示它

CSV File CSV 文件

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

The class model is类模型是

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

Now lets use CsvReader to build the DataTable现在让我们使用 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;
}

To create columns in DataTable e use a bit of reflection, and then use the method GetRecords to add rows in DataTabble要在 DataTable e 中创建列,请使用一些反射,然后使用 GetRecords 方法在 DataTabble 中添加行

using Microsoft.VisualBasic.FileIO;

I would suggest the following.我建议以下。 It should have the advantage at least that ';'它至少应该具有';'的优势in a field will be correctly handled, and it is not constrained to a particular csv format.在一个字段中将被正确处理,并且它不限于特定的 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's Author build functionality in library. CsvHelper 的作者在库中构建功能。 Code became simply:代码变得简单:

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 is used to determine the default delimiter and needs if you want to read csv saved by Excel. CultureInfo.CurrentCulture 用于确定默认分隔符,如果要读取 Excel 保存的 csv,则需要。

I had the same problem but I found a way to use @Alberto Monteiro 's Answer in my own way...我遇到了同样的问题,但我找到了一种以我自己的方式使用@Alberto Monteiro的答案的方法......

My CSV file does not have a "First-Line-Column-Header", I personally didn't put them there for some reasons, So this is the file sample我的 CSV 文件没有“First-Line-Column-Header”,我个人出于某些原因没有将它们放在那里,所以这是文件示例

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

So you got the idea right ?所以你的想法对吗?

Now in I am going to add the Columns manually to the DataTable .现在,我将手动将Columns添加到DataTable And also I am going to use Tasks to do it asynchronously.而且我将使用Tasks异步执行此操作。 and just simply using a foreach loop adding the values into the DataTable.Rows using the following function:并且只需使用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;
    });
}

Now to call the function I simply ButtonClick to do the job现在调用函数我只需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