如何在不创建损坏文件的情况下插入 Excel 个单元格?
How do I insert Excel cells without creating a corrupt file?
我正在使用 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 都会抛出异常。我使用的是每个的最新版本。
您使用的代码存在严重缺陷。这是非常不幸的,因为它来自文档。对于仅使用前 26 列的电子表格,它可能可以正常工作,但在遇到 "wider" 电子表格时会惨败。前 26 列按字母顺序命名,A-Z。第 27-52 列命名为 AA-AZ。第 53-78 列命名为 BA-BZ。 (您应该注意到这种模式。)
单元格 "AA1" 应该 在 所有具有单个字符列名称的单元格之后(即 "A1" - "Z1")。让我们检查比较单元格 "AA1" 和单元格 "B1" 的当前代码。
string.Compare("B1", "AA1", true)
returns值1
- 代码将其解释为 "AA1" 应该放在 单元格 之前 "B1"。
- 调用代码会在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));
}
我正在使用 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 都会抛出异常。我使用的是每个的最新版本。
您使用的代码存在严重缺陷。这是非常不幸的,因为它来自文档。对于仅使用前 26 列的电子表格,它可能可以正常工作,但在遇到 "wider" 电子表格时会惨败。前 26 列按字母顺序命名,A-Z。第 27-52 列命名为 AA-AZ。第 53-78 列命名为 BA-BZ。 (您应该注意到这种模式。)
单元格 "AA1" 应该 在 所有具有单个字符列名称的单元格之后(即 "A1" - "Z1")。让我们检查比较单元格 "AA1" 和单元格 "B1" 的当前代码。
string.Compare("B1", "AA1", true)
returns值1- 代码将其解释为 "AA1" 应该放在 单元格 之前 "B1"。
- 调用代码会在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));
}