2013-09-30 20 views
7

Tôi có đoạn mã bên dưới. Tôi muốn thay thế các văn bản "Text1" bởi "NewText", đó là công việc. Nhưng khi tôi đặt văn bản "Text1" trong một bảng mà không làm việc nữa cho "Text1" bên trong bảng.OpenXML thay thế văn bản trong tất cả tài liệu

Tôi muốn thực hiện thay thế này trong tất cả tài liệu.

using (WordprocessingDocument doc = WordprocessingDocument.Open(String.Format("c:\\temp\\filename.docx"), true)) 
{ 
    var body = doc.MainDocumentPart.Document.Body; 

    foreach (var para in body.Elements<Paragraph>()) 
    { 
     foreach (var run in para.Elements<Run>()) 
     { 
      foreach (var text in run.Elements<Text>()) 
      { 
       if (text.Text.Contains("##Text1##")) 
        text.Text = text.Text.Replace("##Text1##", "NewText"); 
      } 
     } 
    } 
} 

Trả lời

13

Mã của bạn không hoạt động vì các yếu tố bảng (w:tbl) không được chứa trong một yếu tố khoản (w:p). Xem bài viết MSDN sau đây để biết thêm thông tin.

Lớp Text (được tuần tự hóa là w:t) thường đại diện cho văn bản bằng chữ trong phần Run trong tài liệu từ . Vì vậy, bạn chỉ có thể tìm kiếm tất cả w:t yếu tố (Text lớp) và thay thế thẻ của bạn nếu các yếu tố văn bản (w:t) chứa thẻ của bạn:

using (WordprocessingDocument doc = WordprocessingDocument.Open("yourdoc.docx", true)) 
{ 
    var body = doc.MainDocumentPart.Document.Body; 

    foreach (var text in body.Descendants<Text>()) 
    { 
    if (text.Text.Contains("##Text1##")) 
    { 
     text.Text = text.Text.Replace("##Text1##", "NewText"); 
    } 
    } 
} 
+2

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ỏ. –

+0

@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

+0

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. –

3

Có lẽ giải pháp này là dễ dàng hơn

using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(document, true)) 
{ 
string docText = null; 
//1. Copy all the file into a string 
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream())) 
    docText = sr.ReadToEnd(); 

//2. Use regular expression to replace all text 
Regex regexText = new Regex(find); 
docText = regexText.Replace(docText, replace); 

//3. Write the changed string into the file again 
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create))) 
     sw.Write(docText); 
+0

-> http://msdn.microsoft.com/en-us/library/bb508261(v=office.12).aspx – ch2o

+0

Cảnh báo: Điều này cũng thay thế các thẻ xml. Tìm "<" thay thế "string.Empty" kết thúc bằng tài liệu bị hỏng – Marcel

7

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:

  1. 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)
  2. 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)
  3. 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.
  4. 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, ""); 
     } 
    } 
+0

Xin lỗi, tôi gặp lỗi với Visual Studio: 'document.MainDocumentPart.Document.Body' thuộc loại Nội dung, nhưng 'Replace Static void ReplaceText (đoạn văn, chuỗi tìm, chuỗi replaceWith)' yêu cầu một đoạn văn. Vì vậy trình biên dịch dừng và không tiếp tục – Ozeta

+1

@Ozeta, thêm 'var body = doc.MainDocumentPart.Document.Body; var paragraphs = body.Elements (); foreach (var p trong đoạn văn) { ReplaceText (p, replace, with); } ' Tuy nhiên, giải pháp này hoạt động hoàn hảo, nhờ @Amos – ToTa

+0

Cảm ơn cách tiếp cận đoạn văn, đã giúp tôi rất nhiều. – Luntri

Các vấn đề liên quan