本主题演示如何使用 Open XML SDK 使用简单 API for XML (SAX) 方法使用 Open XML SDK 搜索和替换Word文档中的文本。 有关文档的基本结构 WordprocessingML
的详细信息,请参阅 WordprocessingML 文档的结构。
为何使用 SAX 方法?
Open XML SDK 提供两种分析 Office Open XML 文件的方法:文档对象模型 (DOM) 和简单 API for XML (SAX) 。 DOM 方法旨在使用强类型类轻松查询和分析 Open XML 文件。 但是,DOM 方法需要将整个 Open XML 部件加载到内存中,这可能导致处理速度变慢,并在处理非常大的部件时出现内存不足异常。 SAX 方法一次在 Open XML 部件中读取一个元素,而无需将整个部分读取到内存中,从而提供对 XML 数据的非缓存、仅向前访问权限,这使得它在读取非常大的部件时是更好的选择。
访问 MainDocumentPart
Word文档的文本存储在 中MainDocumentPart,因此查找和替换文本的第一步是访问Word文档的 MainDocumentPart
。 为此,我们首先使用 WordprocessingDocument.Open
方法将文档的路径作为第一个参数传入,并使用第二个参数 true
来指示我们要打开文件进行编辑。 然后, MainDocumentPart
确保 不为 null。
// Open the WordprocessingDocument for editing
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(path, true))
{
// Access the MainDocumentPart and make sure it is not null
MainDocumentPart? mainDocumentPart = wordprocessingDocument.MainDocumentPart;
if (mainDocumentPart is not null)
创建内存Stream、OpenXmlReader 和 OpenXmlWriter
使用 DOM 方法来编辑文档时,整个部件将读入内存中,因此,我们可以使用 Open XML SDK 的强类型类访问 Text 类来访问文档的文本并对其进行编辑。 但是,SAX 方法使用 OpenXmlPartReader 和 OpenXmlPartWriter 类,这些类通过仅向前访问权限访问部件的流。 这样做的优点是,不需要将整个部件加载到内存中,这速度更快,占用的内存更少,但由于同一部分不能同时在多个流中打开,因此无法创建 用于 OpenXmlReader 读取部件的 , OpenXmlWriter 而创建 用于同时编辑同一部件的 。 解决方法是创建一个额外的内存流,并将更新的部件写入新的内存流,然后在释放 和 OpenXmlWriter
后OpenXmlReader
使用该流更新部件。 在下面的代码中,我们创建 MemoryStream
用于存储更新的部件,并为 MainDocumentPart
OpenXmlWriter
和 创建 OpenXmlReader
,以便写入MemoryStream
// Create a MemoryStream to store the updated MainDocumentPart
using (MemoryStream memoryStream = new MemoryStream())
{
// Create an OpenXmlReader to read the main document part
// and an OpenXmlWriter to write to the MemoryStream
using (OpenXmlReader reader = OpenXmlPartReader.Create(mainDocumentPart))
using (OpenXmlWriter writer = OpenXmlPartWriter.Create(memoryStream))
读取部件并写入新Stream
现在,我们有了 一个 OpenXmlReader
读取部件,一个 OpenXmlWriter
要写入到新 MemoryStream
,我们可以使用 Read 方法读取部件中的每个元素。 当读取每个元素时,我们检查如果它是类型Text
,如果是,则使用 <xrefDocumentFormat.OpenXml.OpenXmlReader.GetText*> 方法来访问文本并使用 Replace 更新文本。 如果它不是 Text
元素,则我们会将它写入流中,并保持不变。
注意
在Word文档文本可以分为多个Text
元素,因此如果要替换一个短语而不是一个单词,则最好一次替换一个单词。
// Write the XML declaration with the version "1.0".
writer.WriteStartDocument();
// Read the elements from the MainDocumentPart
while (reader.Read())
{
// Check if the element is of type Text
if (reader.ElementType == typeof(Text))
{
// If it is the start of an element write the start element and the updated text
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
string text = reader.GetText().Replace(textToReplace, replacementText);
writer.WriteString(text);
}
else
{
// Close the element
writer.WriteEndElement();
}
}
else
// Write the other XML elements without editing
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
将新Stream写入 MainDocumentPart
将更新的部件写入内存流后,最后一步是将 的位置设置为 MemoryStream
0,并使用 FeedData 方法将 替换为 MainDocumentPart
更新的流。
// Set the MemoryStream's position to 0 and replace the MainDocumentPart
memoryStream.Position = 0;
mainDocumentPart.FeedData(memoryStream);
示例代码
下面是使用 SAX (Simple API for XML) 方法替换Word文档中文本的完整示例代码。
void ReplaceTextWithSAX(string path, string textToReplace, string replacementText)
{
// Open the WordprocessingDocument for editing
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(path, true))
{
// Access the MainDocumentPart and make sure it is not null
MainDocumentPart? mainDocumentPart = wordprocessingDocument.MainDocumentPart;
if (mainDocumentPart is not null)
{
// Create a MemoryStream to store the updated MainDocumentPart
using (MemoryStream memoryStream = new MemoryStream())
{
// Create an OpenXmlReader to read the main document part
// and an OpenXmlWriter to write to the MemoryStream
using (OpenXmlReader reader = OpenXmlPartReader.Create(mainDocumentPart))
using (OpenXmlWriter writer = OpenXmlPartWriter.Create(memoryStream))
{
// Write the XML declaration with the version "1.0".
writer.WriteStartDocument();
// Read the elements from the MainDocumentPart
while (reader.Read())
{
// Check if the element is of type Text
if (reader.ElementType == typeof(Text))
{
// If it is the start of an element write the start element and the updated text
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
string text = reader.GetText().Replace(textToReplace, replacementText);
writer.WriteString(text);
}
else
{
// Close the element
writer.WriteEndElement();
}
}
else
// Write the other XML elements without editing
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
}
// Set the MemoryStream's position to 0 and replace the MainDocumentPart
memoryStream.Position = 0;
mainDocumentPart.FeedData(memoryStream);
}
}
}
}