【问题标题】:Optimizing Listview in C# for large files在 C# 中针对大文件优化 Listview
【发布时间】:2013-12-21 18:12:02
【问题描述】:

我有一个 C# 程序正在拉入一个大约 .csv 文件。 42,000 行长。文件中所有数据的存储方式如下:

Zipcode,City,State

我将所有信息提取到listview 中的三个不同列中。

目前,这些数据需要大约 30 - 50 秒才能进入我的程序。我的问题是如何更好地优化我的代码以缩短这段时间?

以下是我的代码的 sn-p。注释代码是我之前尝试过的代码,但没有成功减少时间,因此我以更易于阅读的方式重写了它。

 //These are globally declared.
lvZip.Columns.Add("Zipcode", 150, HorizontalAlignment.Left);
lvZip.Columns.Add("City", 150, HorizontalAlignment.Left);
lvZip.Columns.Add("State", 150, HorizontalAlignment.Left);
lvZip.View = View.Details;

lvZip.Items.Clear();

        //string dir = System.IO.Path.GetDirectoryName(
        //  System.Reflection.Assembly.GetExecutingAssembly().Location);

        //string path = dir + @"\zip_code_database_edited.csv";
        //var open = new StreamReader(File.OpenRead(path));

        //foreach (String s in File.ReadAllLines(path))
        //{
        //    Zipinfo = s.Split(',');
        //    Zipinfo[0] = Zipinfo[0].Trim();
        //    Zipinfo[1] = Zipinfo[1].Trim();
        //    Zipinfo[2] = Zipinfo[2].Trim();
        //    lvItem = new ListViewItem(Zipinfo);
        //    lvZip.Items.Add(lvItem);
        //}
        //open.Close();

        StreamReader myreader = File.OpenText(path);
        aLine = myreader.ReadLine();

        while (aLine != null)
        {
            Zipinfo = aLine.Split(',');
            Zipinfo[0] = Zipinfo[0].Trim();
            Zipinfo[1] = Zipinfo[1].Trim();
            Zipinfo[2] = Zipinfo[2].Trim();
            lvItem = new ListViewItem(Zipinfo);
            lvZip.Items.Add(lvItem);
            aLine = myreader.ReadLine();
        }
        myreader.Close();

【问题讨论】:

  • 到目前为止的 3 个答案都很好。您可能想尝试 ListView 属性 DoubleBuffered,但如果已经使用 BeginUpdate+EndUpdate,它可能不会有什么不同。如果您认为这种方法值得付出额外努力,您可以使用 VirtualMode 使 ListView 非常 快(例如,在 1 秒内仅加载 1 个“页面”数据)。
  • 使用BeginUpdate()EndUpdate() 将提供更多可见 性能提升。但是 Tweety 建议的 AddRange() 方法将进一步优化您的代码。尽管性能提升可能不像 AddRange() 那样显着 - TextFieldParser 类是专门为处理结构化文本文件(如 CSV)而设计的。

标签: c# listview io streamreader streamwriter


【解决方案1】:

您应该做的是在将任何内容添加到 ListView 之前和之后使用 ListView.BeginUpdate()ListView.EndUpdate()。第二件事是使用ListView.AddRange() 而不是ListView.Add()。通过使用 Add 方法,您将在每次使用 ListView 时重绘它。但是,使用ListView.AddRange() 您只会重绘一次。这应该会为您优化一点。

【讨论】:

    【解决方案2】:

    你可以试试:

    lvZip.BeginUpdate();
    

    在开始添加所有项目之前。

    然后:

    lvZip.EndUpdate();
    

    当你完成时。

    这将阻止控件在添加时绘制每个项目,从而使整个过程非常缓慢。

    【讨论】:

    • 这就是我喜欢stackoverflow的原因。清晰简洁的答案。它有帮助。加载大约需要 5 秒钟。还有其他建议吗?谢谢!
    • 让它更快可能会很棘手。以下是其他建议和时间安排的类似问题:stackoverflow.com/questions/9008310/…
    • 谢谢。我并不是要欺骗已经发布的问题。我做了一些研究,但找不到那个特定的!
    • 不用担心。我认为 5 秒对你的情况来说听起来还不错。
    【解决方案3】:

    黄金法则:不要使用 String.Split() 读取 CSV 数据。

    .NET Framework 已经有一个内置的专用 CSV 解析器,称为 TextFieldParser

    它位于Microsoft.VisualBasic.FileIO 命名空间中。

    String.Split() 不仅有许多边缘情况无法正确处理,而且使用StreamReader 也慢得多。

    最后一点:提示是使用using 语句以确保您的一次性对象得到处置(释放非托管资源)。我看到您没有在上面的代码中使用它们(双关语不是故意的)。

    这实际上并没有超出这个问题的范围,因为有效的内存管理可以提高代码的性能。

    【讨论】:

    • 像 TextFieldParser 这样有用的类在 VB 命名空间中有什么作用?我是 C# 新手,所以我从未听说过它,但从我的研究来看,它看起来很容易。感谢您的提示!
    【解决方案4】:

    也许需要做更多的工作,但使用 DataGridView 并将文本文件用作数据源,您可以在 2 秒内从 42,000 行 .csv 中获得加载时间。下面是一些代码:

        private void button2_Click(object sender, EventArgs e)
        {
            string errorInfo = String.Empty;
            //open text file into Dataset:
            string textFilePath = @"textfile1.csv";
    
            DataSet dataTextFile = new DataSet("textfile");
            if(!LoadTextFile(textFilePath, dataTextFile, out errorInfo))
            {
                MessageBox.Show("Failed to load text file:\n" + errorInfo,
                    "Load Text File");
                return;
            }
            dgTextFile.DataSource = dataTextFile.Tables[0];
            dataTextFile.Dispose(); 
        }
    
        private bool LoadTextFile(string textFilePath, DataSet dataToLoad, out string errorInfo)
        {
            errorInfo = String.Empty;
    
            try
            {
                string textFileFolder = (new System.IO.FileInfo(textFilePath)).DirectoryName;
                string textConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" +
                                                "Data Source=" + textFileFolder + ";" +
                                                "Extended Properties=\"text;\";";
                OleDbConnection textConnection = new OleDbConnection(textConnectionString);
    
                textConnection.Open();
    
                textFilePath = (new System.IO.FileInfo(textFilePath)).Name;
                string selectCommand = "select * from " + textFilePath;
    
                //open command:
                OleDbCommand textOpenCommand = new OleDbCommand(selectCommand);
                textOpenCommand.Connection = textConnection;
    
                OleDbDataAdapter textDataAdapter = new OleDbDataAdapter(textOpenCommand);
    
                int rows = textDataAdapter.Fill(dataToLoad);
    
                textConnection.Close();
                textConnection.Dispose();
    
                return true;
            }
            catch(Exception ex_load_text_file)
            {
                errorInfo = ex_load_text_file.Message;
                return false;
            }
        }
    

    其中一些代码来自 MSDN 示例,但我似乎找不到该页面。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-14
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 2018-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多