【问题标题】:How can I improve the performance of retrieving values from SharedStringTable in OpenXml Excel spreadsheet tools?如何提高在 OpenXml Excel 电子表格工具中从 SharedStringTable 检索值的性能?
【发布时间】:2017-02-28 12:00:18
【问题描述】:

我正在使用 DocumentFormat.OpenXml 读取 Excel 电子表格。用于从 SharedStringTable 对象中查找单元格值的代码存在性能瓶颈(它似乎是某种单元格值的查找表):

var returnValue = sharedStringTablePart.SharedStringTable.ChildElements.GetItem(parsedValue).InnerText;

我创建了一个字典以确保我只检索一次值:

if (dictionary.ContainsKey(parsedValue))
{
    return dictionary[parsedValue];
}

var fetchedValue = sharedStringTablePart.SharedStringTable.ChildElements.GetItem(parsedValue).InnerText;
dictionary.Add(parsedValue, fetchedValue);
return fetchedValue;

这已将执行时间缩短了近 50%。然而,我的指标表明,从SharedStringTable 对象获取值的代码行执行 123,951 次仍需要 208 秒。有没有其他方法可以优化这个操作?

【问题讨论】:

    标签: c# .net excel openxml


    【解决方案1】:

    我会一次性将整个共享字符串表读入您的字典,而不是根据需要查找每个值。这将允许您按顺序浏览文件并存储准备好进行散列查找的值,这将比扫描 SST 以获取所需的每个值更有效。

    在您的流程开始时运行类似以下内容将允许您使用 dictionary[parsedValue] 访问每个值。

    private static void LoadDictionary()
    {
        int i = 0;
    
        foreach (var ss in sharedStringTablePart.SharedStringTable.ChildElements)
        {
            dictionary.Add(i++, ss.InnerText);
        }
    }
    

    如果您的文件非常大,您可能会看到使用 SAX 方法读取文件而不是上面的 DOM 方法会有所收获:

    private static void LoadDictionarySax()
    {
        using (OpenXmlReader reader = OpenXmlReader.Create(sharedStringTablePart))
        {
            int i = 0;
            while (reader.Read())
            {
                if (reader.ElementType == typeof(SharedStringItem))
                {
                    SharedStringItem ssi = (SharedStringItem)reader.LoadCurrentElement();
                    dictionary.Add(i++, ssi.Text != null ? ssi.Text.Text : string.Empty);
                }
            }
        }
    }
    

    在我的机器上,使用具有 60000 行和 2 列的文件,使用上面的 LoadDictionary 方法而不是您问题中的 GetValue 方法,速度快了大约 300 倍。 LoadDictionarySax 方法提供了类似的性能,但在更大的文件(100000 行,10 列)上,SAX 方法比LoadDictionary 方法快约 25%。在更大的文件(100000 行,26 列)上,LoadDictionary 方法引发内存不足异常,但LoadDictionarySax 正常工作。

    【讨论】:

    • 我真的很喜欢这种方法。在 LoadDictionary() 中,如何确保单元格值(数字和查找值)准确映射到字典键?假设 sharedStringTablePart.SharedStringTable.ChildElements 上的枚举器会以从 0 开始的数字顺序返回它们是安全的吗?
    • 很高兴你喜欢它。是的,完全正确,它们将按顺序阅读。出于兴趣,如果您不介意我问,它对您的表现有多大提升?
    • 从 208 秒缩短到 0.9 秒。非常感谢您分享这个技巧,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多