簡體   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