在批量生成Word文檔的應用中,最常見的需求莫過于替換掉文檔中的特定字段以生成新的文檔。利用OpenXML庫可輕松實現(xiàn)這一需求。
僅限文本內(nèi)容的不完善版本
首先放出最簡單然而有bug的版本:
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
//使用OpenXml SDK 2.5打開Word文檔,并將里面的目標字段替換成指定的值
WordprocessingDocument wordDoc = WordprocessingDocument.Open("word.docx", true);
var body = wordDoc.MainDocumentPart!.Document.Body;
var paras = body!.Elements<Paragraph>();
foreach (var para in paras)
{
var runs = para.Elements<Run>();
foreach (var run in runs)
{
var texts = run.Elements<Text>();
foreach (var text in texts)
{
if (text.Text.Contains("首席針頭"))
{
text.Text = text.Text.Replace("首席針頭", "吃席針頭");
}
}
}
}
//保存文檔
wordDoc.Save();
//釋放資源
wordDoc.Dispose();
該版本的原理是遍歷word文檔中的每個段落,搜索段落中的每個文字字段對象,如果找到匹配的值就將其替換成目標值。
該操作存在問題在于有時候看起來連在一起的文字對象是存儲在不同的文字字段對象(run)中的(分開的原因大概率是格式不統(tǒng)一,但有時看著格式完全一樣也會被分開,魔性),比如下面的文檔:
“首席針頭”這個詞組就被分開成“首席”和“針頭”兩個不同的文字字段對象進行存儲。在這種情況下遍歷對象時就無法實現(xiàn)特定字段的匹配。
僅限文本內(nèi)容的完善版本
要解決這個問題也不難,特定字段可能被分開,我們就將段落里面的所有字段記錄下來,然后合并起來看看是否包含該字段。如果是,則檢測特定字段在哪幾個run中,然后將這幾個run合并起來,最后再進行特定字段的替換即可。代碼如下:
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using static System.Net.Mime.MediaTypeNames;
using System.Text;
using Text = DocumentFormat.OpenXml.Wordprocessing.Text;
//合并目標范圍內(nèi)的run
void mergeRuns(IEnumerable<Run> runs, string[] copy_text)
{
//找到特定字段所在的第一個run的位置
int start = 0;
while (true)
{
string sub_str = "";
for (int i = start; i < runs.Count(); i++)
{
sub_str += copy_text[i];
}
if (!sub_str.Contains("首席針頭"))
{
start--;
break;
}
else
{
start++;
}
}
//找到特定字段所在的最后一個run的位置
string inner_str = "";//范圍內(nèi)的字符串
int end = runs.Count();
while (true)
{
string sub_str = "";
for (int i = start; i < end; i++)
{
sub_str += copy_text[i];
}
if (!sub_str.Contains("首席針頭"))
{
end++;
break;
}
else
{
inner_str = sub_str;
end--;
}
}
//將范圍內(nèi)的run合并在一起
int sel_pt = 0;
foreach (var run in runs)
{
if (sel_pt == start)
{
var texts = run.Elements<Text>();
//將run里面的文字改為inner_str的內(nèi)容
int num = 0;
foreach (var mytext in texts)
{
if (num == 0)
{
mytext.Text = inner_str;
}
else
{
mytext.Text = "";
}
num++;
}
}
else if (sel_pt > start && sel_pt < end)//將多余的runs清空
{
var texts = run.Elements<Text>();
foreach (var mytext in texts)
{
mytext.Text = "";
}
}
sel_pt++;
}
}
void replaceTextInParas(IEnumerable<Paragraph> paras)
{
//遍歷文本的所有段落
foreach (var para in paras)
{
while (true)
{
var runs = para.Elements<Run>();
string[] copy_text = new string[runs.Count()];
int pt = 0;
foreach (var run in runs)
{
var texts = run.Elements<Text>();
//第一遍先遍歷,把run內(nèi)部的目標字段替換掉,并構(gòu)建數(shù)組記錄下所有的run
//創(chuàng)建長度和texts個數(shù)一樣的數(shù)組,用于記錄每個text的內(nèi)容
foreach (var text in texts)
{
if (text.Text.Contains("首席針頭"))
{
text.Text = text.Text.Replace("首席針頭", "吃席針頭");
}
copy_text[pt] += text.Text;
}
pt++;
}
//將字符串拼接在一塊,看看是否存在目標字段
string str = string.Join("", copy_text);
//如果存在目標字段,則將范圍內(nèi)的run合并在一起,然后再替換一次,直到不存在目標字段
if (str.Contains("首席針頭"))
{
mergeRuns(runs, copy_text);
}
else
{
break;
}
}
}
}
//文檔讀取
WordprocessingDocument wordDoc = WordprocessingDocument.Open("word.docx", true);
//獲取文檔的主體
var body = wordDoc.MainDocumentPart!.Document.Body;
//獲取文檔的所有段落
var paras = body!.Elements<Paragraph>();
//替換文檔段落中的目標字段
replaceTextInParas(paras);
//保存文檔
wordDoc.Save();
//釋放資源
wordDoc.Dispose();
文本內(nèi)容+表格內(nèi)容的完善版本
當然,該版本僅限于文本內(nèi)容,對于表格中的內(nèi)容是無法替換的,原因在于我們在遍歷替換的時候并沒有遍歷表格中的段落。遍歷表格中的單元格段落寫起來很簡單,完整代碼如下:文章來源:http://www.zghlxwxcb.cn/news/detail-508533.html
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using static System.Net.Mime.MediaTypeNames;
using System.Text;
using Text = DocumentFormat.OpenXml.Wordprocessing.Text;
//合并目標范圍內(nèi)的run
void mergeRuns(IEnumerable<Run> runs, string[] copy_text)
{
//找到特定字段所在的第一個run的位置
int start = 0;
while (true)
{
string sub_str = "";
for (int i = start; i < runs.Count(); i++)
{
sub_str += copy_text[i];
}
if (!sub_str.Contains("首席針頭"))
{
start--;
break;
}
else
{
start++;
}
}
//找到特定字段所在的最后一個run的位置
string inner_str = "";//范圍內(nèi)的字符串
int end = runs.Count();
while (true)
{
string sub_str = "";
for (int i = start; i < end; i++)
{
sub_str += copy_text[i];
}
if (!sub_str.Contains("首席針頭"))
{
end++;
break;
}
else
{
inner_str = sub_str;
end--;
}
}
//將范圍內(nèi)的run合并在一起
int sel_pt = 0;
foreach (var run in runs)
{
if (sel_pt == start)
{
var texts = run.Elements<Text>();
//將run里面的文字改為inner_str的內(nèi)容
int num = 0;
foreach (var mytext in texts)
{
if (num == 0)
{
mytext.Text = inner_str;
}
else
{
mytext.Text = "";
}
num++;
}
}
else if (sel_pt > start && sel_pt < end)//將多余的runs清空
{
var texts = run.Elements<Text>();
foreach (var mytext in texts)
{
mytext.Text = "";
}
}
sel_pt++;
}
}
void replaceTextInParas(IEnumerable<Paragraph> paras)
{
//遍歷文本的所有段落
foreach (var para in paras)
{
while (true)
{
var runs = para.Elements<Run>();
string[] copy_text = new string[runs.Count()];
int pt = 0;
foreach (var run in runs)
{
var texts = run.Elements<Text>();
//第一遍先遍歷,把run內(nèi)部的目標字段替換掉,并構(gòu)建數(shù)組記錄下所有的run
//創(chuàng)建長度和texts個數(shù)一樣的數(shù)組,用于記錄每個text的內(nèi)容
foreach (var text in texts)
{
if (text.Text.Contains("首席針頭"))
{
text.Text = text.Text.Replace("首席針頭", "吃席針頭");
}
copy_text[pt] += text.Text;
}
pt++;
}
//將字符串拼接在一塊,看看是否存在目標字段
string str = string.Join("", copy_text);
//如果存在目標字段,則將范圍內(nèi)的run合并在一起,然后再替換一次,直到不存在目標字段
if (str.Contains("首席針頭"))
{
mergeRuns(runs, copy_text);
}
else
{
break;
}
}
}
}
//文檔讀取
WordprocessingDocument wordDoc = WordprocessingDocument.Open("word.docx", true);
//獲取文檔的主體
var body = wordDoc.MainDocumentPart!.Document.Body;
//獲取文檔的所有段落
var paras = body!.Elements<Paragraph>();
//替換文檔段落中的目標字段
replaceTextInParas(paras);
//獲取文檔的所有表格
var tables = body!.Elements<Table>();
//遍歷表格
foreach (var table in tables)
{
//獲取表格的所有行
var rows = table.Elements<TableRow>();
//遍歷行
foreach (var row in rows)
{
//獲取行的所有單元格
var cells = row.Elements<TableCell>();
//遍歷單元格
foreach (var cell in cells)
{
//獲取單元格的所有段落
var cell_paras = cell.Elements<Paragraph>();
//替換單元格段落中的目標字段
replaceTextInParas(cell_paras);
}
}
}
//保存文檔
wordDoc.Save();
//釋放資源
wordDoc.Dispose();
到此,我們就完美解決了docx文檔(Word文檔)中特定字段的替換問題啦。文章來源地址http://www.zghlxwxcb.cn/news/detail-508533.html
到了這里,關(guān)于使用OpenXML庫替換docx文檔(Word文檔)中的特定字段的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!