Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# OpenXML Excel如何在值位于SharedStringTable中时更改单元格的值_C#_Excel_Openxml - Fatal编程技术网

C# OpenXML Excel如何在值位于SharedStringTable中时更改单元格的值

C# OpenXML Excel如何在值位于SharedStringTable中时更改单元格的值,c#,excel,openxml,C#,Excel,Openxml,我正在寻找一种安全有效的方法来更新文本可能位于SharedStringTable中的单元格的值(这似乎是MS Excel创建的任何电子表格的情况) 顾名思义,SharedStringTable包含可在多个单元格中使用的字符串 因此,仅仅在字符串表中查找该项并更新该值并不是可行的方法,因为其他单元格也可能使用它 据我所知,一个人必须做到以下几点: 检查单元格是否使用字符串表 如果是这样,请检查新字符串是否已经存在,在这种情况下,请使用它(如果其他单元格不再使用该项目,请记住使用旧字符串删除该项目!

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

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

因此,仅仅在字符串表中查找该项并更新该值并不是可行的方法,因为其他单元格也可能使用它

据我所知,一个人必须做到以下几点:

  • 检查单元格是否使用字符串表

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

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

  • 如果是,请使用新字符串创建新项并引用它

  • 如果没有,只需使用新字符串更新现有项

  • 使用OpenXMLSDK有更简单的解决方案吗

    也可以考虑,不仅要更新一个单元格,还需要为多个单元格设置新的(不同)值。


    因此,我们可能在循环中调用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");
       }
    }