更快捷地将csv读取到网格

时间:2013-01-04 12:22:51

标签: c# .net winforms csv datagridview

我在Windows Forms .NET 3.5中有以下内容

对于记录小于10,000的csv,它可以正常工作,但对于30,000以上的记录则较慢。 输入csv文件可以在1 - 1,00,000条记录之间任意记录

目前使用的代码:

/// <summary>
        /// This will import file to the collection object
        /// </summary>
        private bool ImportFile()
        {
            try
            {

                String fName;
                String textLine = string.Empty;
                String[] splitLine;

                // clear the grid view

                accountsDataGridView.Rows.Clear();

                fName = openFileDialog1.FileName;

                if (System.IO.File.Exists(fName))
                {
                    System.IO.StreamReader objReader = new System.IO.StreamReader(fName);

                    do
                    {
                        textLine = objReader.ReadLine();
                        if (textLine != "")
                        {
                            splitLine = textLine.Split(',');
                            if (splitLine[0] != "" || splitLine[1] != "")
                            {
                                accountsDataGridView.Rows.Add(splitLine);
                            }
                        }
                    } while (objReader.Peek() != -1);
                }
                return true;
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("The process cannot access the file"))
                {
                    MessageBox.Show("The file you are importing is open.", "Import Account", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
                else
                {
                    MessageBox.Show(ex.Message);
                }

                return false;
            }

        }

示例输入文件:  
18906,Y
18908,Y
18909,Y
18910,Y
18912,N
18913,N

需要一些关于优化此代码以获得快速读取和建议的建议。在网格中查看。

4 个答案:

答案 0 :(得分:7)

List<string[]> rows = File.ReadAllLines("Path").Select(x => x.Split(',')).ToList();
DataTable dt = new DataTable();
dt.Columns.Add("1");
dt.Columns.Add("2");
rows.ForEach(x => {
  dt.Rows.Add(x);
});
dgv.DataSource = dt;

试试看,我怀疑你在数据网格中有某种形式的列名,现在我只是将它们设为1和2。

要根据原始代码进行过滤,请使用:

List<string[]> rows = File.ReadAllines("Path").Select(x => x.Split(',')).Where(x => x[0] != "" && x[1] != "").ToList();

DataGridView

获取列
  dt.Columns.AddRange(dgv.Columns.Cast<DataGridViewColumn>().Select(x => new DataColumn(x.Name)).ToArray());

答案 1 :(得分:2)

您应该查看VirtualMode of the DataGridView

,而不是将数据直接放入网格中

在你的代码中,你一次做两件事(阅读文件,填充网格),这会导致你的冻结gui。相反,您应该将网格置于虚拟模式,并将BackgroundWorker内的文件读入保存网格数据的列表中。后台工作者可以在每行读取后更新网格的虚拟大小,这允许在网格加载时已经看到数据。通过使用这种方法,您将获得平稳的工作网格。

您将在下面找到一个示例,只需填写一个表单,该表单使用DataGridView,其中包含两个文本列,BackgroundWorkerButton

public partial class FormDemo : Form
{
    private List<Element> _Elements;

    public FormDemo()
    {
        InitializeComponent();
        _Elements = new List<Element>();

        dataGridView.AllowUserToAddRows = false;
        dataGridView.AllowUserToDeleteRows = false;
        dataGridView.ReadOnly = true;
        dataGridView.VirtualMode = true;
        dataGridView.CellValueNeeded += OnDataGridViewCellValueNeeded;

        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.DoWork += OnBackgroundWorkerDoWork;
        backgroundWorker.ProgressChanged += OnBackgroundWorkerProgressChanged;
        backgroundWorker.RunWorkerCompleted += OnBackgroundWorkerRunWorkerCompleted;
    }

    private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
    {
        var filename = (string)e.Argument;

        using (var reader = new StreamReader(filename))
        {
            string line = null;

            while ((line = reader.ReadLine()) != null)
            {
                var parts = line.Split(',');

                if (parts.Length >= 2)
                {
                    var element = new Element() { Number = parts[0], Available = parts[1] };
                    _Elements.Add(element);
                }

                if (_Elements.Count % 100 == 0)
                {
                    backgroundWorker.ReportProgress(0);
                }
            }
        }
    }

    private void OnBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        dataGridView.RowCount = _Elements.Count;
    }

    private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        dataGridView.RowCount = _Elements.Count;
        button.Enabled = true;
    }

    private void OnButtonLoadClick(object sender, System.EventArgs e)
    {
        if (!backgroundWorker.IsBusy
            && DialogResult.OK == openFileDialog.ShowDialog())
        {
            button.Enabled = false;
            backgroundWorker.RunWorkerAsync(openFileDialog.FileName);
        }
    }

    private void OnDataGridViewCellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
    {
        var element = _Elements[e.RowIndex];

        switch (e.ColumnIndex)
        {
            case 0:
                e.Value = element.Number;
                break;

            case 1:
                e.Value = element.Available;
                break;
        }
    }

    private class Element
    {
        public string Available { get; set; }

        public string Number { get; set; }
    }
}

答案 2 :(得分:2)

在速度方面没有太多优化,但追随更具可读性。如果它太慢,可能不是读取文件的方法,而是需要显示&gt; 30k记录的WinForm。

    accountsDataGridView.Rows.Clear();
    using (FileStream file = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096))
    using (StreamReader reader = new StreamReader(file))
    {
        while (!reader.EndOfStream)
        {
            var fields = reader.ReadLine().Split(',');
            if (fields.Length == 2 && (fields[0] != "" || fields[1] != ""))
            {
                accountsDataGridView.Rows.Add(fields);
            }
        }
    }

答案 3 :(得分:2)

您可以尝试使用SuspendLayout()ResumeLayout()方法。

From MSDN Documentation “在调整控件的多个属性时,SuspendLayout和ResumeLayout方法串联使用以抑制多个Layout事件。例如,您通常会调用SuspendLayout方法,然后设置控件的Size,Location,Anchor或Dock属性,然后调用ResumeLayout方法以使更改生效。“

accountsDataGridView.SuspendLayout();
accountsDataGridView.Rows.Clear();

// .....
// in the end after you finished populating your grid call

accountsDataGridView.ResumeLayout();