C# OpenXML Excel如何在值位于SharedStringTable中时更改单元格的值
我正在寻找一种安全有效的方法来更新文本可能位于SharedStringTable中的单元格的值(这似乎是MS Excel创建的任何电子表格的情况) 顾名思义,SharedStringTable包含可在多个单元格中使用的字符串 因此,仅仅在字符串表中查找该项并更新该值并不是可行的方法,因为其他单元格也可能使用它 据我所知,一个人必须做到以下几点:C# OpenXML Excel如何在值位于SharedStringTable中时更改单元格的值,c#,excel,openxml,C#,Excel,Openxml,我正在寻找一种安全有效的方法来更新文本可能位于SharedStringTable中的单元格的值(这似乎是MS Excel创建的任何电子表格的情况) 顾名思义,SharedStringTable包含可在多个单元格中使用的字符串 因此,仅仅在字符串表中查找该项并更新该值并不是可行的方法,因为其他单元格也可能使用它 据我所知,一个人必须做到以下几点: 检查单元格是否使用字符串表 如果是这样,请检查新字符串是否已经存在,在这种情况下,请使用它(如果其他单元格不再使用该项目,请记住使用旧字符串删除该项目!
也可以考虑,不仅要更新一个单元格,还需要为多个单元格设置新的(不同)值。
因此,我们可能在循环中调用updatecell方法…首先考虑一下这个问题。似乎对我的特殊情况有效。 但必须有可能改进,甚至更好,做完全不同的事情:
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 UpdateCell(SharedStringTable SharedStringTable,
字典SheetData、字符串sheetName、,
字符串(参考,字符串文本)
{
Cell Cell=sheetDatas[sheetName].subjections()
.FirstOrDefault(c=>c.CellReference.Value==CellReference);
if(cell==null)返回;
if(cell.DataType==null | | cell.DataType!=CellValues.SharedString)
{
cell.RemoveAllChildren();
AppendChild(新的InlineString(新文本{Text=Text}));
cell.DataType=CellValues.InlineString;
返回;
}
//单元格正在引用字符串表。请检查字符串表中是否已存在新文本,如果已存在,请使用它。
IEnumerable sharedStringItems
=sharedStringTable.Elements();
int i=0;
foreach(SharedStringItem中的SharedStringItem SharedStringItem)
{
if(sharedStringItem.InnerText==文本)
{
cell.CellValue=新的CellValue(i.ToString());
//TODO:应该清理,即从字符串表中删除不再使用的带有旧文本的项。
返回;
}
i++;
}
//字符串表中没有新文本。请检查工作簿中是否有其他单元格引用了具有旧文本的项。
foreach(SheetData中的SheetData SheetData.Values)
{
var cells=sheetData.subjects();
foreach(单元格中的单元格0)
{
如果(cell0.Equals(cell))继续;
if(cell0.DataType!=null
&&cell0.DataType==CellValues.SharedString
&&cell0.CellValue.InnerText==cell.CellValue.InnerText)
{
//其他单元格引用了具有旧文本的项,因此我们无法更新它。请添加新项。
AppendChild(新的SharedStringItem(新文本));
cell.CellValue.Text=(i).ToString();
返回;
}
}
}
//没有其他单元格引用旧项目。请更新它。
sharedStringItems.ElementAt(int.Parse(cell.CellValue.InnerText)).Text=新文本(Text);
}
private静态void DoIt(字符串文件路径)
{
使用(SpreadsheetDocument电子表格=SpreadsheetDocument.Open(文件路径,true))
{
SharedStringTable SharedStringTable
=电子表格.WorkbookPart.GetPartSoftType()
.First().SharedStringTable;
Dictionary sheetDatas=新字典();
foreach(电子表格.WorkbookPart.Workbook.subjections()中的var表)
{
图纸数据图纸数据
=(电子表格.WorkbookPart.GetPartById(sheet.Id)作为工作表部件)
.Worksheet.GetFirstChild();
sheetDatas.Add(sheet.Name,sheetData);
}
UpdateCell(sharedStringTable、sheetDatas、“Sheet1”、“A2”、“Mjau”);
}
}
警告:请勿按原样使用上述内容,它适用于特定的电子表格。如果一个人在其他情况下使用它,很可能是无法处理的事情。
这是我第一次尝试使用OpenXML for电子表格。
最终遵循了乔治·波莱沃的建议。
更容易,而且似乎没有不良副作用(也就是说,在处理电子表格时,有一百万个其他问题需要处理,这些问题可能在您的控制范围之外进行编辑…,因为您可以看到,共享字符串表的更新操作确实让开发人员很忙 根据我的经验,共享字符串表在性能和文件大小经济性方面没有添加任何内容。OpenXml格式在打包容器中被压缩,因此即使有大量重复的字符串,也不会影响文件大小 Microsoft Excel在共享字符串表中写入所有内容,即使没有重复 我建议在修改文档之前将所有内容都转换为
InlineStrings
,进一步的操作就变得简单多了
您可以将其简单地写成InlineStrings
,这将是一个功能相同的文档文件
在编辑文件时,Microsoft Excel会将其转换回共享字符串表,但谁在乎呢
我建议在标准的未来版本中删除共享字符串表功能,除非有合理的基准证明。对于单个单元操作(N个单元数),此解决方案的复杂性为O(N)。我可能会用字典来解决这个问题。想想看。会让生活更轻松。除了稍微大一点的文件之外,仅仅使用内联字符串会有什么负面影响吗?不用管字符串表可以吗?在我的例子中,我有4000+行,其中可能每晚都要更新100个单元格(从数据库中获取数据)。然后,我们让用户在Excel中打开电子表格并进行编辑,从而将内容输入到s中
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");
}
}