因為項目需要通過word模板替換字符串 ,來讓用戶下載word, 就在網上找了找word查找替換字符串的庫或方法,基本上不是收費,就是無實現,或者方法局限性太大
.docx 是通過xml來存儲文字和其他信息的, 有時候一個單詞表面上看到的是一個元素 ,其實內部分裂成了好多元素, 就像下面代碼 ,在word文檔中我們看到的是 abcdefgh,其實是這樣存儲的
<run>
<text>ab</text>
<text>c</text>
</run>
<run>
<text>d</text>
<text>e</text>
</run>
<run>
<text>fgh</text>
</run>
我們要查找替換上面代碼中的 bcdef 就不是簡單的string.replace 了,網上找到了幾個方法都要求 bcdef在一個<text>里面或者一個<run>里面才可以,局限性比較大。索性就自己寫了一個方法
//用於存放多個Text相加之后的尾部 和 替換字符串頭部重疊的Text集合 static List<Text> list = new List<Text>();
private static void Replace(OpenXmlElement parent, string old, string value) { foreach (var child in parent.ChildElements) { //如果是新的段落 清空list if (child is Paragraph) list.Clear(); //如果是Text if (child is Text t) { //Text自身包含old字符串,直接替換 if (t.Text.Contains(old)) t.Text = t.Text.Replace(old, value); //把自身先放入list list.Add(t); //list 中Text元素相加的string var text = string.Join("", list.Select(m => m.Text)); //是否包含舊字符串 var index = text.IndexOf(old); //Text和前面的Text元素(一個或多個)相加包含old字符串 if (index >= 0) { //list第一個元素 刪除舊字符串的的部分 並加上新字符串 list[0].Text = text.Substring(0, index) + value; //list最后一個元素 刪除舊字符串的的部分 t.Text = text.Substring(index + old.Length); //list其他元素 全部替換為空 for (int i = 1; i < list.Count - 1; i++) list[i].Text = ""; //清空list list.Clear(); } //Text與前面Text元素相加的尾部 和 old字符串開頭有重疊 var str = GetSamePart(text, old); //newList var newList = new List<Text>(); //list中的Text相加 結尾和old字符串有重合的的部分, 所引用的list 加入 newlist for (int i = 0; i < list.Count; i++) { if (string.Join("", newList.Select(m => m.Text)).Length >= str.Length) break; newList.Insert(0, list[list.Count - i - 1]); } //list 變成新list list = newList; } //遞歸調用 Replace(child, old, value); } } //獲取前一個字符結尾和后一個字符串開頭重合的部分 public static string GetSamePart(string text, string tempalte) { for (int i = 1; i < tempalte.Length; i++) { var str = tempalte.Substring(0, tempalte.Length - i); if (text.EndsWith(str)) return str; } return ""; }
使用方法如下
using var fs = File.OpenRead("xx.docx"); var ms = new MemoryStream(); await fs.CopyToAsync(ms); using WordprocessingDocument doc = WordprocessingDocument.Open(ms, true); Body body = doc.MainDocumentPart.Document.Body; Replace(body, "bcdef", "1234");
