如何在不创建损坏文件的情况下插入Excel单元格?

时间:2015-11-04 21:16:16

标签: c# excel openxml-sdk

我使用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都会抛出异常。我正在使用每个版本的最新版本。

1 个答案:

答案 0 :(得分:1)

您使用的代码存在严重缺陷。这非常不幸,因为它来自文档。对于仅使用前26列的电子表格,它可能会令人满意,但在面对更宽的&#34;时会失败。电子表格。前26列按字母顺序命名,A-Z。第27-52列命名为AA-AZ。第53-78栏命名为BA-BZ。 (你应该注意到这种模式。)

Cell&#34; AA1&#34; 之后 所有具有单个字符列名称的单元格(即&#34; A1&#34; - &#34; Z1&#34;)。让我们检查比较单元格的当前代码&#34; AA1&#34;与细胞&#34; B1&#34;。

  1. string.Compare("B1", "AA1", true)返回值1
  2. 代码将此解释为&#34; AA1&#34;应放在 单元格&#34; B1&#34;之前。
  3. 调用代码将插入&#34; AA1&#34; 之前&#34; B1&#34;在XML中。
  4. 此时单元格将出现故障,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;
    }
    

    如果您想在列中放置一个新单元格&#34; BC&#34;你正在与细胞进行比较&#34; D5&#34;你会使用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));
    }