如何删除空段落元素?

3

我正在尝试删除包含"{Some Text}"的段落。下面的方法可以实现这一点,但是我注意到在删除段落后,还会剩下空的段落元素。

我该如何通过程序删除<w:p />元素?

以下是我最初用来删除段落的方法。

 using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(file, true))
        {
            MainDocumentPart mainPart = wordDoc.MainDocumentPart;
            Document D = mainPart.Document;

            foreach (Paragraph P in D.Descendants<Paragraph>())
            {
                if (P.InnerText.Contains("{SomeText}"))
                {
                    P.RemoveAllChildren();
                    //P.Remove();   //doesn't remove
                }
            }
            D.Save();
        }

以下是处理后的document.xml文件内容:
<w:p />
<w:p />
<w:p />
<w:p />
<w:p />
<w:p />
<w:p />

1
只是猜测——你是否漏掉了一个“不”字?就像这样 if ( ! P.InnerText.Contains("{SomeText}")) - Bob Kaufman
是的,它们是顶级标签 @rene - Ben Scotch
你的意思是说,它没有被移除?是出现了错误,还是看起来像是无操作? - jn1kk
1
尝试使用D.Remove(P);代替P.RemoveAllChildren - acbod
@DesmondLost:是的,我明白了。你想要删除段落,但是由于这种方法不起作用,所以你只好删除所有子元素。但是这样会在文档中留下一堆空段落。我的回答中提出的建议可以解决这个问题。D.Remove(P) 抛出了一个异常,这实际上是正确的行为。这就是你原来代码中 P.Remove() 应该做的事情。 - Matt Burland
显示剩余9条评论
2个回答

7
这里的问题是:
        foreach (Paragraph P in D.Descendants<Paragraph>())
        {
            if (P.InnerText.Contains("{SomeText}"))
            {
                P.Remove();   //doesn't remove
            }
        }

您是否正在迭代集合时尝试删除其中的一个项目。由于某些奇怪的原因,OpenXML SDK在此处实际上不会抛出异常,而只是悄悄退出foreach循环。使用调试器并逐步执行将向您展示这一点。解决方法很简单:

        foreach (Paragraph P in D.Descendants<Paragraph>().ToList())
        {
            if (P.InnerText.Contains("{SomeText}"))
            {
                P.Remove();   //will now remove
            }
        }

通过添加 ToList(),您正在将段落复制(浅复制)到单独的列表中,并对该列表进行迭代。现在,当您删除一个段落时,它会从 D.Descendants<Paragraph>() 集合中移除,但不会从您的列表中移除,迭代将继续进行。

虽然这似乎是一个合乎逻辑的答案,但很难相信它正在退出循环。 - jn1kk
@jsn:我知道,这很奇怪,但它确实是这样。如果你附加调试器并逐步执行,在第一种情况下一旦它到达P.Remove(),它将跳转到循环的闭合括号,然后是下一条语句。它真的应该抛出一个异常,但我猜有人在那方面犯了错误。 - Matt Burland
你说得对,马特,它确实有效。有点不相关,但是有没有一种方法可以遍历文档并删除所有的<w:p />元素(即在创建文档时按Enter键)? - Ben Scotch
2
这段代码可以获取所有空段落。foreach (Paragraph P in D.Descendants<Paragraph>().Where(x => !x.HasChildren).ToList()) @DesmondLost - acbod
@DesmondLost:删除所有空的?还是所有段落?无论哪种方式,只需要在循环中更改(或删除)条件即可。 - Matt Burland
那个答案真的帮助我从我的模板中删除了一些段落。 - Birol Capa

0
上面的答案帮助我创建了以下代码片段,该片段删除了从开始到结束(不包括开始和结束)的段落。当您必须使用模板作为输入,但又不想在输出中使用其中某些部分时,这种方法非常方便。
public void RemoveParagraphsFromDocument(string begin, string end)
{
    using (var wordDoc = WordprocessingDocument.Open(OutputPath, true))
    {
        var mainPart = wordDoc.MainDocumentPart;
        var doc = mainPart.Document;
        var paragraphs = doc.Descendants<Paragraph>().ToList();
        var beginIndex = paragraphs.FindIndex(par => par.InnerText.Equals(begin));
        var endIndex = paragraphs.FindIndex(par => par.InnerText.Equals(end));

        for (var i = beginIndex + 1; i < endIndex; i++)
        {
            paragraphs[i].Remove();
        }

        doc.Save();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接