OpenXML标签搜索

我正在编写一个.NET应用程序,该应用程序应读取200页长(通过DocumentFormat.OpenXML

2.5)附近的.docx文件,以查找该文档应包含的某些标记的所有出现。为了清楚起见,我不是在寻找OpenXML标记,而是应该由文档编写者将其设置为文档中的值的占位符,我需要在第二阶段中填写这些标记。此类标签应采用以下格式:

 <!TAG!>

(其中TAG可以是任意字符序列)。正如我所说,我必须找到所有此类标签的出现,再加上(如果可能的话)将找到该标签出现的“页面”定位。我在网上发现了一些东西,但是不只一次,基本方法是将文件中的所有内容转储为字符串,然后在这样的字符串中查找,而不管.docx编码如何。这可能导致误报或完全不匹配(而测试.docx文件包含多个标签),其他示例可能只是我对OpenXML的了解而已。查找此类标签的正则表达式模式应为此类:

<!(.)*?!>

可以在整个文档中找到标签(在表,文本,段落中以及页眉和页脚中)。

我正在Visual Studio 2013 .NET 4.5中进行编码,但是如果需要的话我可以回来。PS我不希望使用Office Interop

API的代码,因为目标平台将无法运行Office。

我可以产生的最小的.docx示例将其存储在文档内部

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 wp14">

<w:body>

<w:p w:rsidR="00CA7780" w:rsidRDefault="00815E5D">

<w:pPr>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

</w:pPr>

<w:r>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

<w:t>TRY</w:t>

</w:r>

</w:p>

<w:p w:rsidR="00815E5D" w:rsidRDefault="00815E5D">

<w:pPr>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

</w:pPr>

<w:proofErr w:type="gramStart"/>

<w:r>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

<w:t>&lt;!TAG1</w:t>

</w:r>

<w:proofErr w:type="gramEnd"/>

<w:r>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

<w:t>!&gt;</w:t>

</w:r>

</w:p>

<w:p w:rsidR="00815E5D" w:rsidRPr="00815E5D" w:rsidRDefault="00815E5D">

<w:pPr>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

</w:pPr>

<w:r>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

<w:t>TRY2</w:t>

</w:r>

<w:bookmarkStart w:id="0" w:name="_GoBack"/>

<w:bookmarkEnd w:id="0"/>

</w:p>

<w:sectPr w:rsidR="00815E5D" w:rsidRPr="00815E5D">

<w:pgSz w:w="11906" w:h="16838"/>

<w:pgMar w:top="1417" w:right="1134" w:bottom="1134" w:left="1134" w:header="708" w:footer="708" w:gutter="0"/>

<w:cols w:space="708"/>

<w:docGrid w:linePitch="360"/>

</w:sectPr>

</w:body>

</w:document>

最好的问候,迈克

回答:

尝试查找标签的问题在于单词并非总是以其在Word中的格式出现在基础XML中。例如,在您的示例XML中,<!TAG1!>标记被分成多个运行,如下所示:

<w:r>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

<w:t>&lt;!TAG1</w:t>

</w:r>

<w:proofErr w:type="gramEnd"/>

<w:r>

<w:rPr>

<w:lang w:val="en-GB"/>

</w:rPr>

<w:t>!&gt;</w:t>

</w:r>

正如评论中指出的那样,这有时是由拼写和语法检查程序引起的,但这并不是所有可能导致这种情况的原因。例如,在标签的各个部分上使用不同的样式也可能会导致它。

处理的方法之一是找到InnerTextParagraph和比较,对你的Regex。该InnerText属性将返回段落的纯文本,而不会妨碍基础文档中的任何格式或其他XML。

有了标签后,替换文本是下一个问题。由于上述原因,您不能只InnerText用一些新文本替换,因为不清楚文本的哪些部分属于哪个部分Run。解决此问题的最简单方法是删除所有现有Run的,并Run使用Text包含新文本的属性添加新的。

以下代码显示查找标签并立即替换它们,而不是按照您在问题中建议的那样使用两次传递。说实话,这只是为了简化示例。它应该显示您需要的一切。

private static void ReplaceTags(string filename)

{

Regex regex = new Regex("<!(.)*?!>", RegexOptions.Compiled);

using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(filename, true))

{

//grab the header parts and replace tags there

foreach (HeaderPart headerPart in wordDocument.MainDocumentPart.HeaderParts)

{

ReplaceParagraphParts(headerPart.Header, regex);

}

//now do the document

ReplaceParagraphParts(wordDocument.MainDocumentPart.Document, regex);

//now replace the footer parts

foreach (FooterPart footerPart in wordDocument.MainDocumentPart.FooterParts)

{

ReplaceParagraphParts(footerPart.Footer, regex);

}

}

}

private static void ReplaceParagraphParts(OpenXmlElement element, Regex regex)

{

foreach (var paragraph in element.Descendants<Paragraph>())

{

Match match = regex.Match(paragraph.InnerText);

if (match.Success)

{

//create a new run and set its value to the correct text

//this must be done before the child runs are removed otherwise

//paragraph.InnerText will be empty

Run newRun = new Run();

newRun.AppendChild(new Text(paragraph.InnerText.Replace(match.Value, "some new value")));

//remove any child runs

paragraph.RemoveAllChildren<Run>();

//add the newly created run

paragraph.AppendChild(newRun);

}

}

}

上述方法的缺点是,您可能拥有的所有样式都会丢失。这些可以从现有Run的中复制,但是如果有多个Run具有不同属性的,则需要计算出哪些属性需要复制到哪里。Run如果需要的话,没有什么可以阻止您在上面的代码中创建多个具有不同属性的。其他元素也将丢失(例如,任何符号),因此也需要考虑这些元素。

以上是 OpenXML标签搜索 的全部内容, 来源链接: utcz.com/qa/399556.html

回到顶部