Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何读取Excel工作表并异步写入文件?_C#_.net_Multithreading_File Handling - Fatal编程技术网

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

C# 如何读取Excel工作表并异步写入文件?,c#,.net,multithreading,file-handling,C#,.net,Multithreading,File Handling,我得到了一个非常大的Excel工作簿,其中包含500多张工作表。每个工作表表示一个存储,行包含该存储的事务。每个图纸布局都是相同的。我被要求编写一个程序,在每张纸上循环,提取特定的事务数据,并将所有内容写入一个巨大的CSV文件 我知道这种功能更适合于关系数据库,但我一直被要求按原样处理它 我已经编写了一个程序,成功地解析并写入了数据。问题是,同步读取和写入数据时,完成文件写入几乎需要半个小时 我希望通过异步读取和写入每个工作表中的数据来完成此任务。在C#中,我更愿意使用任务并行库来实现这一点,但

我得到了一个非常大的Excel工作簿,其中包含500多张工作表。每个工作表表示一个存储,行包含该存储的事务。每个图纸布局都是相同的。我被要求编写一个程序,在每张纸上循环,提取特定的事务数据,并将所有内容写入一个巨大的CSV文件

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

我已经编写了一个程序,成功地解析并写入了数据。问题是,同步读取和写入数据时,完成文件写入几乎需要半个小时

我希望通过异步读取和写入每个工作表中的数据来完成此任务。在C#中,我更愿意使用任务并行库来实现这一点,但我也愿意接受其他选项

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

foreach( Worksheet ws in _excelApp.Worksheets)
{
    Parallel.Invoke(()=>ExportWorksheet(ws));
}
然后在方法中(为简洁起见缩短):

private void导出工作表(工作表ws)
{         
使用(FileStream fs=new-new-FileStream(fi.FullName,FileMode.Append,FileAccess.Write,FileShare.Write,1,true))
{
用于(int行=1;行<300;行++)
{
对于(int列=1;列<20)
{
byte[]bytes=Encoding.ASCII.GetBytes(ws.Cells[行,列].Value.ToString()+“,”);
写(bytes,0,bytes.count());
}
fs.Write(Encoding.ASCII.GetBytes(“\n”),0,2);
} 
}
}
当然,这给了我奇怪的结果

我走对了吗?我应该使用不同的编码吗?有没有更干净的方法来完成异步写入?这里有违反线程规则的地方吗


欢迎所有建议。感谢您的帮助。

您最好使用范围的Value属性(例如工作表的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));
DateTime start=DateTime.Now;
//使用(FileStream fs=newfilestream(@“C:\temp\x.x”,FileMode.Append,FileAccess.Write,FileShare.Write,1,true))
//{
//用于(int row=1;row<3*1000;row++)
//    {
//用于(int列=1;列<3*1000;列++)
//        {
//byte[]bytes=Encoding.ASCII.GetBytes(1.ToString()+“,”);
//fs.Write(字节,0,字节.长度);
//        }
//byte[]bytes2=Encoding.ASCII.GetBytes(“\n”);
//fs.Write(字节2,0,字节2.Length);
//    }
//}
使用(TextWriter tw=new StreamWriter(new FileStream(@“C:\temp\x.x”,FileMode.Append,FileAccess.Write,FileShare.Write,1,true)))
{
用于(int row=1;row<3*1000;row++)
{
用于(int列=1;列<3*1000;列++)
{
tw.Write(1.ToString());
tw.写(',');
}
tw.WriteLine();
}
}
DateTime end=DateTime.Now;
Show(string.Format(“花费的时间:{0:N0}ms.”,(end-start.total毫秒));
第一部分(与您的代码几乎相同,现在已注释掉)需要3.670秒(是的,超过3000秒)。
第二部分(未注释掉)需要12秒。

我从C#中读取Excel的经验通常是令人讨厌的。你所有的计算时间都花在了Excel上——写出CSV文件根本不需要时间。不值得为单独的线程而烦恼


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

好建议。我在实现范围[row,columns]时遇到了一些困难,因为每次使用它时都会出现COM错误。工作表.Cells[行,列]没有给我错误信息。而且,真正的工作表并不像示例那样整洁;需要收集特定范围的细胞。我会调查的。谢谢。你不应该使用Range[row,column],而应该使用Range.Value,它返回一个对象[,](或者在最新版本的框架中可能是动态的),如果这样做有效,那么这个答案确实很好。是的,但是我主要关心的是异步文件写入。@field_b:我很确定您走错了方向-异步写入文件很可能不会给您带来合理的性能增益。您的瓶颈似乎与C#应用程序和Excel进程之间的COM调用数量有关(我过去经常看到这是一个瓶颈)。每个
ws.Cells[row,column]
都会创建这样一个COM调用,这意味着“进程间通信”,这本身就很慢。这就是为什么Patrick的提示会对您有所帮助:
ActiveSheet.Range.Value
将在一次COM调用中为您提供一张工作表的全部数据。
        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));