2015-11-04 111 views
1

我正在使用OpenXML SDK来更新Excel电子表格的内容。将单元格插入Excel行时,必须按正确的顺序插入,否则文件将无法在Excel中正确打开。我正在使用以下代码来查找将插入单元格后的第一个单元格。此代码来几乎直接从OpenXML SDK documentation如何插入Excel单元而不创建损坏的文件?

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

您使用的代码严重瑕疵。这非常不幸,因为它来自文档。对于仅使用前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中的“B1”之前插入“AA1”

此时单元格将出现故障,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)); 
}