【问题标题】:Reading an Excel file and writing to text file very slow using c#使用 c# 读取 Excel 文件并写入文本文件非常慢
【发布时间】:2016-07-13 08:35:26
【问题描述】:

我在 c# wpf 应用程序中有以下代码。我正在读取 Excel 文件,删除隐藏字符并尝试保留单元格格式,然后将数据写入管道分隔的文本文件。这段代码看起来很简单,但速度很慢。关于我为什么以及如何改进流程的任何想法?

    private void ReadWriteExcelData(string strFileName)
    {
        Excel.Application xlApp;
        Excel.Workbook xlWorkBook;
        Excel.Worksheet xlWorkSheet;
        Excel.Range range, colrange, rowrange;

        xlApp = new Excel.Application();
        xlWorkBook = xlApp.Workbooks.Open(strFileName, 0, true, 5, "", "", true,
            Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);

        Excel.Sheets excelSheets = xlWorkBook.Worksheets;
        if (blnLetExcelDecide)
            {
                range = xlWorkSheet.UsedRange;
            }
            else
            {
                Excel.Range c1 = xlWorkSheet.Cells[lngExcelStartRow, strExcelStartCol];
                Excel.Range c2 = xlWorkSheet.Cells[lngExcelEndRow, strExcelEndCol];
                range = (Excel.Range)xlWorkSheet.get_Range(c1, c2);
            }

            colrange = range.Columns;
            lngNumCols = colrange.Count;
            rowrange = range.Rows;
            lngNumRows = rowrange.Count;

            object[,] values = (object[,])range.Value;
            string[] Fields = new string[lngNumCols];
            int NumRow = 1;

            while (NumRow <= values.GetLength(0))
            {
                strDataRow = "";

                for (lngColCnt = 1; lngColCnt <= lngNumCols; lngColCnt++)
                {
                    strCellData = range[NumRow, lngColCnt].Text;
                    strCellData = strCellData.TrimStart(' ');


                    if (strCellData == null)
                    {
                        strCellData = string.Empty;
                    }
                    else
                    {
                        strCellData = strCellData.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ");
                    }

                    if (lngColCnt == lngNumCols)
                    {
                        strDataRow += strCellData;
                    }
                    else
                    {
                        strDataRow += strCellData + "|";
                    }
                }

                WriteDataRow(strDataRow, strFullOutputFileName);

                if (NumRow % intModNumber == 0)
                {
                    dblProgressPct = ((double)NumRow / (double)lngNumRows);
                    dblProgress = Math.Round((dblProgressPct * 100), 0);
                    prgIndicator.Width = dblProgress * 4;
                    lblPrctPrgrs.Content = dblProgress + "%";

                    grdProgressIndicator.InvalidateVisual();
                    System.Windows.Forms.Application.DoEvents();
                }

                NumRow++;
            }
       }  

这是 WriteDataRow 例程:

    public void WriteDataRow(string strDataRow, string strFullFileName)
    {
        using (StreamWriter file = new StreamWriter(@strFullFileName, true, Encoding.GetEncoding("iso-8859-1")))
        {
            file.WriteLine(strDataRow);
        }
    }

【问题讨论】:

  • 多少数据?有多慢?如果你把“写”注释掉,它仍然很慢吗?
  • 您将范围内容拉入values,但循环范围以获取单元格内容 - 为什么不为此使用values?编辑:没关系 - 你想要文本而不是值...... C# 和 Excel 之间的任何行程都需要跨两个进程之间的边界编组调用:这有很多开销。
  • 抱歉没有给出文件大小。我正在使用的 Excel 文件是 68 列 x 3652 行。执行大约需要 5 1/2 分钟。
  • 你当然可以在其中使用一些StringBuilders,替换 strCellData 和 strDataRow 连接
  • 作为一个测试 - 如果你使用 values 数组代替单个 Text 值,性能如何?

标签: c# wpf excel


【解决方案1】:

这是一种方法,涉及使用一些 VBA 来读取所有单元格的文本值。

首先,在常规模块中创建一个包含此函数的 xlsm 文件:

Public Function GetText(strWB As String, strSheet As String, _
                        strAddress As String) As Variant()
    Dim rng As Range, arr() As Variant, r As Long, c As Long
    Set rng = Workbooks(strWB).Worksheets(strSheet).Range(strAddress)
    rng.Columns.AutoFit 'avoid getting "######" !
    ReDim arr(0 To rng.Rows.Count - 1, 0 To rng.Columns.Count - 1)
    For r = 1 To rng.Rows.Count
    For c = 1 To rng.Columns.Count
        arr(r - 1, c - 1) = rng.Cells(r, c).Text
    Next c
    Next r
    GetText = arr
End Function

打开数据文件后,用宏打开文件:

Excel.Workbook xlCodeWb = xlApp.Workbooks.Open(@"D:\Folder\Stuff\TheMacro.xlsm"); 

然后调用宏:

object[,] values = xlApp.Run("'" + xlCodeWb.Name + "'!GetText", 
                   xlWorkBook.Name, xlWorkSheet.Name, range.Address); 

values 现在是表单中所有文本值的二维数组,无需在跨进程边界的单独调用中挑选出每个值的开销。您可以遍历数组并将“清理后”的值写入文件。

顺便说一句,您可能应该考虑在 main 方法中打开并写入输出文件:打开它一次,然后写入行,仅在完成后关闭它。无需为每一行重新打开它。

【讨论】:

  • 这个 Excel 宏听起来不错,但是如何将这个应用程序和宏一起分发?
  • 我对分发 C# 桌面应用程序不太熟悉,但您可以将它作为资源添加到您的表单应用程序中吗?然后,您可以在运行时将其提取到临时位置。
  • 我想我找到了解决办法。我这样做的原因 strCellData = range[NumRow, lngColCnt].Text;是因为当一个字段的 fromat 类型为 "#,##0.00_)[Red](#,##0.00) 时,strCellData 中的结果包含 #,##0.00_)[Red](#,##0.00) . 我所做的是: if (strNumberFormat.IndexOf("[Red]") > 0 ) { strCellData = Convert.ToString(range[NumRow, lngColCnt].Value2); }
【解决方案2】:

我添加了对“#,##0.00_)Red 格式类型的检查。仅当此单元格具有此格式时,我才执行 Convert.ToString(range[NumRow, lngColCnt].Value2)。

【讨论】:

    猜你喜欢
    • 2019-04-30
    • 2016-10-10
    • 1970-01-01
    • 2021-05-09
    • 2021-10-12
    • 2020-04-23
    • 2013-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多