【问题标题】:How do I insert Excel cells without creating a corrupt file?如何在不创建损坏文件的情况下插入 Excel 单元格?
【发布时间】:2017-03-23 01:29:54
【问题描述】:

我正在使用 OpenXML SDK 来更新 Excel 电子表格的内容。将单元格插入 Excel 行时,它们必须以正确的顺序插入,否则文件将无法在 Excel 中正确打开。我正在使用以下代码来查找将在我插入的单元格之后的第一个单元格。这段代码几乎直接来自OpenXML SDK documentation

public static Cell GetFirstFollowingCell(Row row, string newCellReference)
{
    Cell refCell = null;
    foreach (Cell cell in row.Elements<Cell>())
    {
        if (string.Compare(cell.CellReference.Value, newCellReference, true) > 0)
        {
            refCell = cell;
            break;
        }
    }

    return refCell;
}

当我使用此代码编辑文件然后在 Excel 中打开它们时,Excel 报告文件已损坏。 Excel 能够修复文件,但大部分数据已从工作簿中删除。为什么会导致文件损坏?

旁注:在转向痛苦的低级 OpenXML SDK 之前,我尝试了两个不同的 .NET Excel 库。 NPOI 创建了损坏的电子表格,每当我尝试保存时,EPPlus 都会抛出异常。我使用的是每个版本的最新版本。

【问题讨论】:

    标签: c# excel openxml-sdk


    【解决方案1】:

    您使用的代码存在严重缺陷。这是非常不幸的,因为它来自文档。对于只使用前 26 列的电子表格,它可能可以正常工作,但在遇到“更宽”的电子表格时会惨遭失败。前 26 列按字母顺序命名,A-Z。第 27-52 列被命名为 AA-AZ。 53-78 列被命名为 BA-BZ。 (你应该注意到这种模式。)

    单元格“AA1”应该出现在之后所有具有单个字符列名称的单元格(即“A1”-“Z1”)。让我们检查比较单元格“AA1”和单元格“B1”的当前代码。

    1. string.Compare("B1", "AA1", true) 返回值 1
    2. 代码将此解释为意味着“AA1”应放置在之前单元格“B1”。
    3. 调用代码将在 XML 中插入“AA1”之前“B1”。

    此时单元格将出现故障并且 Excel 文件已损坏。显然,string.Compare 本身不足以确定一行中单元格的正确顺序。需要进行更复杂的比较。

    public static bool IsNewCellAfterCurrentCell(string currentCellReference, string newCellReference)
    {
        var columnNameRegex = new Regex("[A-Za-z]+");
        var currentCellColumn = columnNameRegex.Match(currentCellReference).Value;
        var newCellColumn = columnNameRegex.Match(newCellReference).Value;
        var currentCellColumnLength = currentCellColumn.Length;
        var newCellColumnLength = newCellColumn.Length;
        if (currentCellColumnLength == newCellColumnLength)
        {
            var comparisonValue = string.Compare(currentCellColumn, newCellColumn, StringComparison.OrdinalIgnoreCase);
            return comparisonValue > 0;
        }
    
        return currentCellColumnLength < newCellColumnLength;
    }
    

    如果您想在“BC”列中放置一个新单元格,并且要与单元格“D5”进行比较,则可以使用IsCellAfterColumn("D5", "BC5")。将新的比较函数代入原代码并用LINQ进行简化:

    public static Cell GetFirstFollowingCell(Row row, string newCellReference)
    {
        var rowCells = row.Elements<Cell>();
        return rowCells.FirstOrDefault(c => IsNewCellAfterCurrentCell(c.CellReference.Value, newCellReference));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 2013-12-14
      • 2018-08-24
      相关资源
      最近更新 更多