C#Excel Interop循环遍历单元格时速度慢

pww*_*lff 8 c# excel performance office-interop

我试图从C#中的Excel文档中提取所有文本数据,并且遇到了性能问题.在下面的代码中,我打开工作簿,遍历所有工作表,并循环使用范围内的所有单元格,随时从每个单元格中提取文本.问题是,这需要14秒才能执行.

public class ExcelFile
{
    public string Path = @"C:\test.xlsx";
    private Excel.Application xl = new Excel.Application();
    private Excel.Workbook WB;
    public string FullText;
    private Excel.Range rng;
    private Dictionary<string, string> Variables;
    public ExcelFile()
    {
        WB = xl.Workbooks.Open(Path);
        xl.Visible = true;
        foreach (Excel.Worksheet CurrentWS in WB.Worksheets)
        {
            rng = CurrentWS.UsedRange;
            for (int i = 1; i < rng.Count; i++)
            { FullText += rng.Cells[i].Value; }
        }
        WB.Close(false);
        xl.Quit();
    }
}
Run Code Online (Sandbox Code Playgroud)

而在VBA中我会做这样的事情,需要大约1秒钟:

Sub run()
    Dim strText As String
    For Each ws In ActiveWorkbook.Sheets
        For Each c In ws.UsedRange
            strText = strText & c.Text
        Next c
    Next ws
End Sub
Run Code Online (Sandbox Code Playgroud)

或者,甚至更快(少于1秒):

Sub RunFast()
    Dim strText As String
    Dim varCells As Variant
    For Each ws In ActiveWorkbook.Sheets
        varCells = ws.UsedRange
        For i = 1 To UBound(varCells, 1)
            For j = 1 To UBound(varCells, 2)
                strText = strText & CStr(varCells(i, j))
            Next j
        Next i
    Next ws
End Sub
Run Code Online (Sandbox Code Playgroud)

也许在C#的for循环中发生了一些我不知道的事情?是否可以将一个范围加载到一个数组类型的对象中(如我在上一个例子中),以允许迭代仅仅值,而不是单元格对象?

Ant*_*sek 5

我用这个功能。循环仅用于从索引 0 开始转换为数组,主要工作在object[,] tmp = range.Value.

public object[,] GetTable(int row, int col, int width, int height)
{
    object[,] arr = new object[height, width];

    Range c1 = (Range)Worksheet.Cells[row + 1, col + 1];
    Range c2 = (Range)Worksheet.Cells[row + height, col + width];
    Range range = Worksheet.get_Range(c1, c2);

    object[,] tmp = range.Value;

    for (int i = 0; i < height; ++i)
    {
        for (int j = 0; j < width; ++j)
        {
            arr[i, j] = tmp[i + tmp.GetLowerBound(0), j + tmp.GetLowerBound(1)];
        }
    }                 

    return arr;
}
Run Code Online (Sandbox Code Playgroud)


Joh*_* Wu 5

Excel 和 C# 完全运行在不同的环境中。C# 使用托管内存在 .NET 框架中运行,而 Excel 是本机 C++ 应用程序并在非托管内存中运行。在这两者之间转换数据(称为“编组”的过程)在性能方面非常昂贵。

调整您的代码无济于事。与编组过程相比,for 循环、字符串构造等都非常快。获得显着更好性能的唯一方法是减少必须跨越进程间边界的行程数。逐个单元地提取数据永远不会获得您想要的性能。

这里有几个选项:

  1. 在 VBA 中编写一个可以执行您想要的所有操作的子或函数,然后通过互操作调用该子或函数。 演练

  2. 使用互操作将工作表保存为 CSV 格式的临时文件,然后使用 C# 打开该文件。您将需要遍历并解析文件以将其转换为有用的数据结构,但此循环将进行得更快。

  3. 使用互操作将一系列单元格保存到剪贴板,然后使用 C# 直接读取剪贴板。