Vay trên một số câu trả lời khác ở những nơi khác nhau, và với thực tế là bốn chướng ngại vật chính phải được khắc phục:
- Xóa mọi hig Các ký tự Unicode mức h từ chuỗi thay thế của bạn không thể đọc từ Word (từ đầu vào người dùng xấu)
- Khả năng tìm kiếm kết quả tìm kiếm của bạn trên nhiều lần chạy hoặc phần tử văn bản trong một đoạn (Word thường chia nhỏ một câu chạy văn bản)
- Khả năng bao gồm ngắt dòng trong văn bản thay thế của bạn để chèn văn bản nhiều dòng vào tài liệu.
- Khả năng chuyển vào bất kỳ nút nào làm điểm bắt đầu cho tìm kiếm của bạn để hạn chế tìm kiếm phần đó của tài liệu (chẳng hạn như phần thân, đầu trang, chân trang, bảng cụ thể, hàng trong bảng hoặc tablecell) .
Tôi chắc chắn các kịch bản nâng cao như dấu trang, lồng ghép phức tạp sẽ cần sửa đổi nhiều hơn, nhưng nó hoạt động cho các loại tài liệu văn bản cơ bản mà tôi đã chạy cho đến nay và hữu ích hơn nhiều bỏ qua chạy hoàn toàn hoặc sử dụng RegEx trên toàn bộ tệp mà không có khả năng nhắm mục tiêu một phần TableCell hoặc Tài liệu cụ thể (cho các kịch bản nâng cao).
Ví dụ Cách sử dụng:
var body = document.MainDocumentPart.Document.Body;
ReplaceText(body, replace, with);
Mã:
using System;
using System.Collections.Generic;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace My.Web.Api.OpenXml
{
public static class WordTools
{
/// <summary>
/// Find/replace within the specified paragraph.
/// </summary>
/// <param name="paragraph"></param>
/// <param name="find"></param>
/// <param name="replaceWith"></param>
public static void ReplaceText(Paragraph paragraph, string find, string replaceWith)
{
var texts = paragraph.Descendants<Text>();
for (int t = 0; t < texts.Count(); t++)
{ // figure out which Text element within the paragraph contains the starting point of the search string
Text txt = texts.ElementAt(t);
for (int c = 0; c < txt.Text.Length; c++)
{
var match = IsMatch(texts, t, c, find);
if (match != null)
{ // now replace the text
string[] lines = replaceWith.Replace(Environment.NewLine, "\r").Split('\n', '\r'); // handle any lone n/r returns, plus newline.
int skip = lines[lines.Length - 1].Length - 1; // will jump to end of the replacement text, it has been processed.
if (c > 0)
lines[0] = txt.Text.Substring(0, c) + lines[0]; // has a prefix
if (match.EndCharIndex + 1 < texts.ElementAt(match.EndElementIndex).Text.Length)
lines[lines.Length - 1] = lines[lines.Length - 1] + texts.ElementAt(match.EndElementIndex).Text.Substring(match.EndCharIndex + 1);
txt.Space = new EnumValue<SpaceProcessingModeValues>(SpaceProcessingModeValues.Preserve); // in case your value starts/ends with whitespace
txt.Text = lines[0];
// remove any extra texts.
for (int i = t + 1; i <= match.EndElementIndex; i++)
{
texts.ElementAt(i).Text = string.Empty; // clear the text
}
// if 'with' contained line breaks we need to add breaks back...
if (lines.Count() > 1)
{
OpenXmlElement currEl = txt;
Break br;
// append more lines
var run = txt.Parent as Run;
for (int i = 1; i < lines.Count(); i++)
{
br = new Break();
run.InsertAfter<Break>(br, currEl);
currEl = br;
txt = new Text(lines[i]);
run.InsertAfter<Text>(txt, currEl);
t++; // skip to this next text element
currEl = txt;
}
c = skip; // new line
}
else
{ // continue to process same line
c += skip;
}
}
}
}
}
/// <summary>
/// Determine if the texts (starting at element t, char c) exactly contain the find text
/// </summary>
/// <param name="texts"></param>
/// <param name="t"></param>
/// <param name="c"></param>
/// <param name="find"></param>
/// <returns>null or the result info</returns>
static Match IsMatch(IEnumerable<Text> texts, int t, int c, string find)
{
int ix = 0;
for (int i = t; i < texts.Count(); i++)
{
for (int j = c; j < texts.ElementAt(i).Text.Length; j++)
{
if (find[ix] != texts.ElementAt(i).Text[j])
{
return null; // element mismatch
}
ix++; // match; go to next character
if (ix == find.Length)
return new Match() { EndElementIndex = i, EndCharIndex = j }; // full match with no issues
}
c = 0; // reset char index for next text element
}
return null; // ran out of text, not a string match
}
/// <summary>
/// Defines a match result
/// </summary>
class Match
{
/// <summary>
/// Last matching element index containing part of the search text
/// </summary>
public int EndElementIndex { get; set; }
/// <summary>
/// Last matching char index of the search text in last matching element
/// </summary>
public int EndCharIndex { get; set; }
}
} // class
} // namespace
public static class OpenXmlTools
{
// filters control characters but allows only properly-formed surrogate sequences
private static Regex _invalidXMLChars = new Regex(
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]",
RegexOptions.Compiled);
/// <summary>
/// removes any unusual unicode characters that can't be encoded into XML which give exception on save
/// </summary>
public static string RemoveInvalidXMLChars(string text)
{
if (string.IsNullOrEmpty(text)) return "";
return _invalidXMLChars.Replace(text, "");
}
}
Nguồn
2015-04-27 20:01:15
Lưu ý rằng câu trả lời này và tất cả các câu trả lời khác mà chỉ cần lấy một khối chủ yếu là làm việc nhưng chúng không phải là rất đáng tin cậy. Có rất nhiều thứ trong OpenXml có thể phá vỡ văn bản. Áp dụng định dạng cho một phần của từ, dấu trang, v.v. tất cả chia nhỏ văn bản. Mã tại https://msdn.microsoft.com/en-us/library/ee441250%28v=office.12%29.aspx?f=255&MSPPError=-2147217396 được cho là sửa lỗi, nhưng tôi chưa thực hiện nó vì vậy không thể báo cáo thành công hay thất bại. Trong các tài liệu mẫu cụ thể của tôi, khoảng 1 từ trong số 100-200 được chia nhỏ. –
@WadeHatler: Cảm ơn bạn đã bình luận. Tôi sẽ xem xét mã được cung cấp trong liên kết của bạn. – Hans
Rất vui được trợ giúp.Tôi đã kết luận rằng tôi ghét OpenXml. Tôi đã tìm thấy một số mã hầu như hoạt động tại http://blogs.msdn.com/b/ericwhite/archive/2008/07/09/open-xml-sdk-and-linq-to-xml.aspx, http://blogs.msdn.com/b/ericwhite/archive/2008/03/14/technical-improvements-in-the-open-xml-sdk.aspx và http://blogs.msdn.com/b/ericwhite/ lưu trữ/2009/02/16/finding-paragraphs-by-style-name-hoặc-content-in-an-mở-xml-word-processing-document.aspx. Nó vẫn không đáng tin cậy bởi vì nó không thể tìm ra khi nào để vào không gian để tôi có được những mảnh vỡ. Tôi sẽ đăng câu trả lời nếu tôi làm cho nó hoạt động đúng. –