如何读取Excel工作表并异步写入文件?

时间:2014-01-01 10:05:13

标签: c# .net multithreading file-handling

我收到了一张非常大的Excel工作簿,其中包含500多张。每个工作表代表一个商店,行包含该商店的交易。每个工作表布局都相同。我被要求编写一个循环遍历每个工作表的程序,提取特定的事务数据,并将所有内容写入一个巨大的CSV文件。

我知道这种功能更适合关系数据库,但我被要求按原样处理。

我编写了一个成功解析数据并编写数据的程序。问题是,在同步读取和写入数据时,完成文件写入需要将近半个小时。

我想通过异步读取和写入每张表中的数据来完成此任务。在C#中,我更喜欢使用任务并行库,但我可以使用其他选项。

我正在考虑从foreach循环中分离工作线程,如下所示:

foreach( Worksheet ws in _excelApp.Worksheets)
{
    Parallel.Invoke(()=>ExportWorksheet(ws));
}

然后在方法中(为简洁起见缩短):

private void ExportWorksheet(Worksheet ws)
{         
     using(FileStream fs = new new FileStream(fi.FullName, FileMode.Append, FileAccess.Write, FileShare.Write, 1, true))
     {
         for(int row = 1; row < 300; row++)
         {
              for(int column = 1; column < 20)
              {
                   byte[] bytes = Encoding.ASCII.GetBytes(ws.Cells[row, column].Value.ToString() + ",");
                   fs.Write(bytes, 0, bytes.count());
              }

              fs.Write(Encoding.ASCII.GetBytes("\n"), 0, 2);
         } 
     }

}

当然,这给了我奇怪的结果。

我是否在正确的轨道上?我应该使用不同的编码吗?是否有更简洁的方法来完成异步写入?这里是否有任何线程规则被破坏?

欢迎所有建议。谢谢你的帮助。

2 个答案:

答案 0 :(得分:2)

不是循环遍历行和列,而是最好使用范围的Value属性(例如WorkSheet的ActiveRange)。它包含一个包含所有数据的二维数组。这会使读数性能提高1000倍。

另一部分。我将它重写为两部分,删除Excel引用:

        DateTime start = DateTime.Now;

        //using (FileStream fs = new FileStream(@"C:\temp\x.x", FileMode.Append, FileAccess.Write, FileShare.Write, 1, true))
        //{
        //    for (int row = 1; row < 3 * 1000; row++)
        //    {
        //        for (int column = 1; column < 3 * 1000; column++)
        //        {
        //            byte[] bytes = Encoding.ASCII.GetBytes(1.ToString() + ",");
        //            fs.Write(bytes, 0, bytes.Length);
        //        }

        //        byte[] bytes2 = Encoding.ASCII.GetBytes("\n");
        //        fs.Write(bytes2, 0, bytes2.Length);
        //    }
        //}

        using (TextWriter tw = new StreamWriter(new FileStream(@"C:\temp\x.x", FileMode.Append, FileAccess.Write, FileShare.Write, 1, true)))
        {
            for (int row = 1; row < 3 * 1000; row++)
            {
                for (int column = 1; column < 3 * 1000; column++)
                {
                    tw.Write(1.ToString());
                    tw.Write(',');
                }

                tw.WriteLine();
            }
        }

        DateTime end = DateTime.Now;

        MessageBox.Show(string.Format("Time spent: {0:N0} ms.", (end - start).TotalMilliseconds));

第一部分(几乎与您的代码完全相同,现已注释掉)需要3.670(是的,超过三千)秒。 第二部分(未注释掉)需要12秒。

答案 1 :(得分:0)

我从C#阅读Excel的经验通常是令人讨厌的。所有计算时间都花在使用Excel进行广告投放上 - 写出CSV文件根本不需要时间。用单独的线程来打扰是不值得的。

在某些情况下,我只是将电子表格保存为.csv,然后从那里解析它。如何使用我不知道的多个工作表,但您可以通过工作表翻页将它们逐个保存到.CSV。然后,将.CSV作为长字符串读取并清理它们。