OpenXML Excel当值在SharedStringTable中时如何更改单元格的值

时间:2015-11-15 11:33:24

标签: c# excel openxml

我正在寻找一种安全有效的方法来更新文本可能位于SharedStringTable中的单元格的值(这似乎是MS Excel创建的任何电子表格的情况)。

顾名思义,SharedStringTable包含可在多个单元格中使用的字符串。

所以只是找到字符串表中的项目并更新值不是它可能被其他单元格使用的方式。

据我所知,必须做以下事情:

  1. 检查单元格是否正在使用字符串表

  2. 如果是这样,检查新字符串是否已经存在,在这种情况下只使用它(如果任何其他单元格不再使用旧字符串,请记得删除该字符串!)

  3. 如果没有,请检查电子表格中的任何其他单元格是否引用了旧字符串的项目

  4. 如果是这样,请使用新字符串创建新项目并引用它

  5. 如果没有,只需使用新字符串

  6. 更新现有项目

    使用OpenXML SDK有没有更简单的解决方案?

    还要考虑一个人可能不仅要更新一个单元格,而是要为几个单元格设置新的(不同的)值。 所以我们可能在循环中调用更新单元格方法......

2 个答案:

答案 0 :(得分:2)

首先考虑一下。似乎适用于我的特定情况。 但必须有可能改进,或者甚至更好地完全不同:

private static void UpdateCell(SharedStringTable sharedStringTable, 
   Dictionary<string, SheetData> sheetDatas, string sheetName, 
   string cellReference, string text)
{
   Cell cell = sheetDatas[sheetName].Descendants<Cell>()
    .FirstOrDefault(c => c.CellReference.Value == cellReference);
   if (cell == null) return;
   if (cell.DataType == null || cell.DataType != CellValues.SharedString)
   {
    cell.RemoveAllChildren();
    cell.AppendChild(new InlineString(new Text { Text = text }));
    cell.DataType = CellValues.InlineString;
    return;
   }
   // Cell is refering to string table. Check if new text is already in string table, if so use it.
   IEnumerable<SharedStringItem> sharedStringItems 
    = sharedStringTable.Elements<SharedStringItem>();
   int i = 0;
   foreach (SharedStringItem sharedStringItem in sharedStringItems)
   {
    if (sharedStringItem.InnerText == text)
    {
       cell.CellValue = new CellValue(i.ToString());
       // TODO: Should clean up, ie remove item with old text from string table if it is no longer in use.
       return;
    }
    i++;
   }
   // New text not in string table. Check if any other cells in the Workbook referes to item with old text.
   foreach (SheetData sheetData in sheetDatas.Values)
   {
    var cells = sheetData.Descendants<Cell>();
    foreach (Cell cell0 in cells)
    {
       if (cell0.Equals(cell)) continue;
       if (cell0.DataType != null 
       && cell0.DataType == CellValues.SharedString 
       && cell0.CellValue.InnerText == cell.CellValue.InnerText)
       {
        // Other cells refer to item with old text so we cannot update it. Add new item.
        sharedStringTable.AppendChild(new SharedStringItem(new Text(text)));
        cell.CellValue.Text = (i).ToString();
        return;
       }
    }
   }
   // No other cells refered to old item. Update it.
   sharedStringItems.ElementAt(int.Parse(cell.CellValue.InnerText)).Text = new Text(text);
}

...

private static void DoIt(string filePath)
{
   using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(filePath, true))
   {
    SharedStringTable sharedStringTable 
       = spreadSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>()
        .First().SharedStringTable;
    Dictionary<string, SheetData> sheetDatas = new Dictionary<string, SheetData>();
    foreach (var sheet in spreadSheet.WorkbookPart.Workbook.Descendants<Sheet>())
    {
       SheetData sheetData 
        = (spreadSheet.WorkbookPart.GetPartById(sheet.Id) as WorksheetPart)
           .Worksheet.GetFirstChild<SheetData>();
       sheetDatas.Add(sheet.Name, sheetData);
    }
    UpdateCell(sharedStringTable, sheetDatas, "Sheet1", "A2", "Mjau");
   }
}

警告:请勿按原样使用上述内容,它适用于特定的电子表格。如果在其他情况下使用它,很可能无法处理。 这是我第一次尝试使用OpenXML进行电子表格。 结束了George Polevoy提出的建议。 更容易,并且似乎没有不良副作用(也就是说,在操纵电子表格时还有一百万个其他问题需要处理,可能会在你的控制之外进行编辑......)

答案 1 :(得分:1)

正如您所看到的,共享字符串表的更新操作确实让开发人员忙碌。

根据我的经验,共享字符串表不会在性能和文件大小经济方面添加任何内容。无论如何,OpenXml格式都在包装容器内压缩,所以即使你有大量重复的字符串,它也不会影响文件大小。

Microsoft Excel将所有内容写入共享字符串表中,即使没有重复。

我建议您在修改文档之前将所有内容转换为InlineStrings,然后进一步操作变得非常简单。

您可以将其简单地写为InlineStrings,这将是一个功能相同的文档文件。

Microsoft Excel会在编辑文件时将其转换回共享字符串表,但是谁在乎。

我建议在标准的未来版本中删除共享字符串表功能,除非有一些合理的基准测试证明。