在 C# 中格式化 excel 中多行的最快方法

Fastest way to format multiple rows in an excel in C#

我有一个巨大的数据集,我想将其写入 Excel 并且需要根据业务逻辑对行执行条件格式设置。因此,对于数据插入部分,我使用数据数组来填充 Excel 并且它工作得非常快。但是,在格式化行时,我发现性能严重下降。仅仅进行格式化就花费了两倍多的时间。

截至目前,我正在将格式应用于各个行并循环遍历一系列行。但是,我想知道我是否可以一次 select 多行并对这些行应用批量格式化选项: 这是我现在拥有的:

foreach (int row in rowsToBeFormatted)
{
    Excel.Range range = (Excel.Range)xlsWorksheet.Range[xlsWorksheet.Cells[row + introFormat, 1], xlsWorksheet.Cells[row + introFormat, 27]];
    range.Font.Size = 11;
    range.Interior.ColorIndex = 15;
    range.Font.Bold = true;
}

下面是我如何尝试 select 多行到范围并应用格式的演示:

string excelrange = "A3:AA3,A83:AA83,A88:AA88,A94:AA94,A102:AA102,A106:AA106,A110:AA110,...." (string with more than 3000 characters)
xlsWorksheet.get_Range(excelrange).Interior.Color = Color.SteelBlue;

但是,当我执行代码时出现以下错误:

Exception from HRESULT: 0x800A03EC

并且内部异常没有任何内容。我有什么想法可以达到预期的结果吗?

根据问题下的评论,范围字符串有 hard-coded 255 个字符的限制,但我找不到任何相关文档。另一位评论者建议使用分号作为分隔符,但 the documentation 明确指出逗号应用作范围字符串中的联合运算符:

The name of the range in A1-style notation in the language of the application. It can include the range operator (a colon), the intersection operator (a space), or the union operator (a comma). It can also include dollar signs, but they are ignored. You can use a local defined name in any part of the range. If you use a name, the name is assumed to be in the language of the application.

那么我们从这里去哪里呢?单独格式化每个范围确实效率低下。 Application接口提供了方法Union,但是循环调用和单独格式化一样效率低下。因此,自然的选择是将范围字符串限制为最大值,从而最大限度地减少对 COM 接口的调用次数。

您可以拆分整个范围以格式化成块;每个不超过 255 个字符的限制。我会使用枚举器来实现它:

static IEnumerable<string> GetChunks(IEnumerable<string> ranges)
{
    const int MaxChunkLength = 255;
    var sb = new StringBuilder(MaxChunkLength);
    foreach (var range in ranges)
    {
        if (sb.Length > 0)
        {
            if (sb.Length + range.Length + 1 > MaxChunkLength)
            {
                yield return sb.ToString();
                sb.Clear();
            }
            else
            {
                sb.Append(",");
            }
        }
        sb.Append(range);
    }
    if (sb.Length > 0)
    {
        yield return sb.ToString();
    }
}

var rowsToFormat = new[] { 3, 83, 88, 94, 102, 106, 110/*, ...*/ }
var rowRanges = rowsToFormat.Select(row => "A" + row + ":" + "AA" + row);

foreach (var chunk in GetChunks(rowRanges))
{
    var range = xlsWorksheet.Range[chunk];
    // do formatting stuff here
}

以上比单独格式化快10-15倍:

foreach (var rangeStr in rowRanges)
{
    var range = xlsWorksheet.Range[rangeStr];
    // do formatting stuff here
}

我还可以进一步了解 space 优化,例如对连续行进行分组,但如果您使用小计格式化离散行,它就无济于事了。