2013-09-30 11 views
7

मेरे पास नीचे कोड का टुकड़ा है। मैं "टेक्स्ट 1" टेक्स्ट को "न्यूटेक्स्ट" द्वारा प्रतिस्थापित करना चाहता हूं, यह काम है। लेकिन जब मैं एक तालिका में "टेक्स्ट 1" टेक्स्ट डालता हूं जो तालिका के अंदर "टेक्स्ट 1" के लिए अब काम नहीं करता है।ओपनएक्सएमएल सभी दस्तावेज़ों में पाठ को प्रतिस्थापित करता है

मैं इस दस्तावेज़ को सभी दस्तावेज़ों में बनाना चाहता हूं।

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"); 
      } 
     } 
    } 
} 

उत्तर

13

क्योंकि तालिका तत्व (w:tbl) में एक पैराग्राफ तत्व (w:p) निहित नहीं होने पर आपका कोड काम नहीं करता। अधिक जानकारी के लिए निम्नलिखित MSDN आलेख देखें।

Text वर्ग (w:t के रूप में श्रृंखलाबद्ध) आम तौर पर एक शब्द दस्तावेज़ में एक Run तत्व के भीतर शाब्दिक पाठ प्रतिनिधित्व करता है। तो अगर आप बस सभी w:t तत्वों के लिए खोज (Text वर्ग) और अपने टैग को बदलने सकता है अगर पाठ तत्व (w:t) अपने टैग है:

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

ध्यान दें कि यह जवाब और अन्य सभी जवाब है कि सिर्फ एक ब्लॉक हड़पने ज्यादातर काम करते हैं लेकिन वे बहुत विश्वसनीय नहीं कर रहे हैं। ओपनएक्सएमएल में बहुत सी चीजें हैं जो पाठ को तोड़ सकती हैं। शब्द, बुकमार्क, आदि के भाग में स्वरूपण लागू करना सभी पाठ को तोड़ देते हैं। Https://msdn.microsoft.com/en-us/library/ee441250%28v=office.12%29.aspx?f=255&MSPPError=-2147217396 पर कोड माना जाता है कि यह ठीक है, लेकिन मैंने इसे अभी तक काम नहीं किया है इसलिए सफलता या विफलता की रिपोर्ट नहीं कर सकते हैं। मेरे विशेष नमूना दस्तावेजों में, 100-200 में से लगभग 1 शब्द टूट जाता है। –

+0

@WadeHatler: आपकी टिप्पणी के लिए धन्यवाद। मैं आपके लिंक में दिए गए कोड पर एक नज़र डालेगा। – Hans

+0

मदद करने के लिए खुश।मैंने ज्यादातर निष्कर्ष निकाला है कि मैं ओपनएक्सएमएल से नफरत करता हूं। मुझे कुछ कोड मिला जो लगभग 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 और http://blogs.msdn.com/b/ericwhite/ संग्रह/2009/02/-16/खोज-पैराग्राफों-दर-शैली के नाम या सामग्री में एक-खुले एक्सएमएल-शब्द-संसाधन-document.aspx। यह अभी भी अविश्वसनीय है क्योंकि यह पता नहीं लगा सकता कि रिक्त स्थान कब रखना है, इसलिए मुझे टुकड़े मिलते हैं। अगर मैं इसे सही काम कर रहा हूं तो मैं जवाब पोस्ट करूंगा। –

3

हो सकता है कि इस समाधान आसान

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

चेतावनी: यह xml टैग को भी प्रतिस्थापित करता है। एक खोज "<" के साथ एक की जगह "string.Empty" एक भ्रष्ट दस्तावेज़ – Marcel

7

पर उधार है विभिन्न स्थानों में कुछ अन्य उत्तरों, और इस तथ्य के साथ कि चार मुख्य बाधाओं को दूर किया जाना चाहिए:

  1. किसी भी hig को हटाएं आपकी प्रतिस्थापन स्ट्रिंग से एच स्तर यूनिकोड वर्ण जिन्हें Word (खराब उपयोगकर्ता इनपुट से) से पढ़ा नहीं जा सकता है
  2. अनुच्छेद के भीतर एकाधिक रन या टेक्स्ट तत्वों में आपके खोज परिणाम की खोज करने की क्षमता (शब्द अक्सर कई वाक्यों में एक वाक्य को तोड़ देगा टेक्स्ट रन)
  3. दस्तावेज़ में बहु-पंक्ति पाठ डालने के लिए अपने प्रतिस्थापन टेक्स्ट में एक लाइन ब्रेक शामिल करने की क्षमता।
  4. किसी भी नोड में अपनी खोज के शुरुआती बिंदु के रूप में पास करने की क्षमता ताकि दस्तावेज़ के उस भाग (जैसे शरीर, हेडर, पाद लेख, एक विशिष्ट तालिका, तालिका पंक्ति, या टेबलसेल) पर खोज को प्रतिबंधित किया जा सके। ।

मुझे यकीन है कि बुकमार्क, जटिल घोंसले के उन्नत परिदृश्यों को इस पर अधिक संशोधन की आवश्यकता होगी, लेकिन यह अब तक चलने वाले मूल शब्द दस्तावेज़ों के प्रकार के लिए काम कर रहा है, और मेरे लिए बहुत उपयोगी है अनदेखा रन पूरी तरह से या पूरी फ़ाइल पर एक RegEx का उपयोग कर किसी विशिष्ट टेबलसेल या दस्तावेज़ भाग (उन्नत परिदृश्यों के लिए) को लक्षित करने की कोई क्षमता नहीं है।

उदाहरण उपयोग:

var body = document.MainDocumentPart.Document.Body; 
ReplaceText(body, replace, with); 

कोड:

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

मुझे माफ साथ समाप्त होता है, मैं दृश्य स्टूडियो के साथ एक त्रुटि मिलती है: 'document.MainDocumentPart.Document.Body' शारीरिक प्रकार का है, लेकिन 'सार्वजनिक स्थैतिक शून्य ReplaceText (अनुच्छेद अनुच्छेद, स्ट्रिंग खोज, स्ट्रिंग प्रतिस्थापित) के साथ एक पैराग्राफ की आवश्यकता है। तो कंपाइलर बंद हो जाता है और – Ozeta

+1

@ ओज़ेटा जारी नहीं रखता है, यह 'var body = doc.MainDocumentPart.Document.Body जोड़ें; var अनुच्छेद = शरीर। एलिमेंट्स (); foreach (अनुच्छेदों में var पी) { ReplaceText (पी, प्रतिस्थापित, के साथ); } ' हालांकि, यह समाधान सही काम करता है, धन्यवाद @ एमोस – ToTa

+0

अनुच्छेद दृष्टिकोण के लिए धन्यवाद, मुझे बहुत मदद मिली। – Luntri

संबंधित मुद्दे