wei*_*gyn 10 .net c# ms-word openxml
我正在编写一个.NET应用程序,它应该读取200页长的.docx文件(通过DocumentFormat.OpenXML 2.5)来查找文档应该包含的某些标记的所有出现.为了清楚起见,我不是在寻找OpenXML标签,而是寻找应该由文档编写者设置到文档中的标签,作为我需要在第二阶段填写的值的占位符.此类标签应采用以下格式:
<!TAG!>
Run Code Online (Sandbox Code Playgroud)
(其中TAG可以是任意字符序列).正如我所说,我必须找到所有这些标签的出现加上(如果可能的话)找到已找到标签出现的"页面".我在Web上发现了一些东西,但不止一次基本方法是将文件的所有内容转储到字符串中,然后查看这样的字符串,无论.docx编码如何.这或者导致误报或者根本没有匹配(虽然测试.docx文件包含多个标签),其他示例可能与我对OpenXML的了解有点差异.找到这样的标签的正则表达式模式应该是这样的:
<!(.)*?!>
Run Code Online (Sandbox Code Playgroud)
标签可以在整个文档中找到(在表格,文本,段落内,也可以在页眉和页脚中).
我在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>
Run Code Online (Sandbox Code Playgroud)
最诚挚的问候,迈克
尝试查找标记的问题是,单词并不总是在它们看起来在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>
Run Code Online (Sandbox Code Playgroud)
正如评论中所指出的,这有时是由拼写和语法检查引起的,但并不是所有这些都可能导致它.例如,在标签的部分上具有不同的样式也可能导致它.
处理此问题的一种方法是找到InnerTexta 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 Code Online (Sandbox Code Playgroud)
上述方法的唯一缺点是你可能拥有的任何款式都将丢失.这些可以从现有Run的复制,但如果有多个Run具有不同的属性,你需要找出你需要复制的地方.Run如果需要的话,没有什么可以阻止你在上面的代码中创建多个具有不同属性的代码.
| 归档时间: |
|
| 查看次数: |
4791 次 |
| 最近记录: |