C# 在DataGridView中读取CSV文件

C# 在DataGridView中读取CSV文件,c#,csv,datagridview,C#,Csv,Datagridview,我想将csv文件读入Datagridview。我希望有一个类和一个函数,可以像这样读取csv: class Import { public DataTable readCSV(string filePath) { DataTable dt = new DataTable(); using (StreamReader sr = new StreamReader(filePath)) {

我想将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的第一个解决方案 下面是使用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,但现在我们跳过(1),这会跳过标题行,现在我们将使用Select创建
集合
,然后再次调用ToList,最后调用ForEach在DataTable中添加行。.NET 4.0中提供了File.ReadLines

Obs.:不会读取所有行,它返回一个IEnumerable,并且行是惰性计算的,因此只会加载第一行两次

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

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

对文件执行LINQ to Objects查询以获得其行的过滤集

使用file.writeAllines(字符串, 方法,或使用file.AppendAllLines(字符串, IEnumerable)方法

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

此方法使用UTF8作为编码值

如果您还有任何疑问,请查看以下答案:

使用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来构建数据表

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;
}
公共数据表readCSV(字符串文件路径)
{
var dt=新数据表();
var csv=新的CsvReader(新的StreamReader(filePath));
//创建列
typeof(Person).GetProperties().Select(p=>p.Name).ToList().ForEach(x=>dt.Columns.Add(x));
//添加行
csv.GetRecords().ToList.ForEach(line=>dt.Rows.Add(line.Name、line.Age、line.Birthdate、line.Working));
返回dt;
}

要在DataTable e中创建列,请使用一些反射,然后使用GetRecords方法在DataTable中添加行,使用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;
    } 
}
公共类CsvImport
{
公共静态数据表NewDataTable(字符串文件名、字符串分隔符、bool firstRowContainsFieldNames=true)
{
DataTable结果=新DataTable();
使用(TextFieldParser tfp=新的TextFieldParser(文件名))
{
tfp.SetDelimiters(分隔符);
//获取一些列名
如果(!tfp.EndOfData)
{
string[]fields=tfp.ReadFields();
对于(int i=0;i
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,则需要该分隔符。

您似乎在为每个标记向表中添加一行,而不是为文件中的每一行。另外,您可以将数据绑定到
DataGrid
,而无需迭代itDestic。您看到我的答案了吗?有什么反馈吗?我原以为你需要空条目。还是我读错了?@HughJones下面的字符串
abc;abc;;abc
如果按
拆分
您将有4个条目=>
abc | abc | | abc
,如果您不想有空条目,请使用StringSplitOptions.RemoveEmptyEntries。所以在本例中,我将更改答案,在行中我想要空条目,但在标题中,我不需要。在这两个行中,我都会想。如果标题中的字段数与行中的字段数不匹配,则不希望出现这种情况。很好的解决方案,+1对
File.ReadLines的两次调用不会读取整个文件两次吗?使用反射的性能损失是什么?@aardila File.ReadLines不会读取所有行,它返回一个IEnumerable,并且行是延迟计算的,因此只会加载第一行两次。Will更改了我的答案来解释这一点。谢谢,这看起来是一种可靠的方法,但我在哪里可以找到VisualBasic.FileIO命名空间?在您的project@Destic-关于可靠性;首先有一个基本假设,即加载的csv文件是有效的。您的方法工作得很好。我只是在结尾编辑了while,因为我想让datatable包含行作为结果<代码>w
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);
    }
}