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><!TAG1</w:t>
</w:r>
<w:proofErr w:type="gramEnd"/>
<w:r>
<w:rPr>
<w:lang w:val="en-GB"/>
</w:rPr>
<w:t>!></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><!TAG1</w:t>
</w:r>
<w:proofErr w:type="gramEnd"/>
<w:r>
<w:rPr>
<w:lang w:val="en-GB"/>
</w:rPr>
<w:t>!></w:t>
</w:r>
正如评论中指出的那样,这有时是由拼写和语法检查程序引起的,但这并不是所有可能导致这种情况的原因。例如,在标签的各个部分上使用不同的样式也可能会导致它。
处理的方法之一是找到InnerText
的Paragraph
和比较,对你的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