2009-05-11 13 views
16

एक बहुत अच्छा diff गूगल यहाँ द्वारा की मेजबानी वर्ग नहीं है:मैं एक्सेल मैक्रो के भीतर जावास्क्रिप्ट का उपयोग कैसे कर सकता हूं?

http://code.google.com/p/google-diff-match-patch/

मैं इसे पहले का उपयोग किया है कुछ वेब साइटों पर, लेकिन अब मैं एक एक्सेल मैक्रो के भीतर यह उपयोग करने के लिए पाठ की तुलना करनी होती दो कोशिकाओं के बीच।

हालांकि, यह केवल जावास्क्रिप्ट, पायथन, जावा और सी ++ में उपलब्ध है, वीबीए नहीं।

मेरे उपयोगकर्ता Excel 2003 तक सीमित हैं, इसलिए एक शुद्ध .NET समाधान काम नहीं करेगा। कोड को वीबीए में मैन्युअल रूप से अनुवाद करने में बहुत अधिक समय लगेगा और अपग्रेड करना मुश्किल होगा।

एक विकल्प जिसे मैंने माना था, .NET कंपाइलर्स (JScript.NET या J #) का उपयोग कर जावास्क्रिप्ट या जावा स्रोत को संकलित करना था, VB.NET के रूप में आउटपुट करने के लिए परावर्तक का उपयोग करें, फिर आखिरकार VB.NET कोड को वीबीए में डाउनग्रेड करें, मुझे एक शुद्ध वीबीए समाधान दे रहा है। किसी भी .NET संकलक के साथ संकलित करने में समस्याएं होने के बाद, मैंने इस पथ को छोड़ दिया।

मान लीजिए कि मैं एक काम कर रहा था .NET लाइब्रेरी, मैं ExcelDna (http://www.codeplex.com/exceldna) का उपयोग भी कर सकता था, एक ओपन-सोर्स एक्सेल ऐड-इन, .NET कोड एकीकरण को आसान बनाने के लिए।

मेरा अंतिम विचार इंटरनेट एक्सप्लोरर ऑब्जेक्ट होस्ट करना था, इसे जावास्क्रिप्ट स्रोत भेजना और उसे कॉल करना था। यहां तक ​​कि अगर मुझे यह काम करने के लिए मिला, तो मेरा अनुमान है कि यह गंदगी-धीमी और गन्दा होगी।

अद्यतन: समाधान मिला!

मैंने स्वीकार किए गए उत्तर से नीचे वर्णित WSC विधि का उपयोग किया। मैं डिफ साफ करने के लिए एक छोटे से और मुझे वापस दे सरणियों की एक VBA संगत सरणी WSC कोड बदलना पड़ा:

function DiffFast(text1, text2) 
{ 
    var d = dmp.diff_main(text1, text2, true); 
    dmp.diff_cleanupSemantic(d); 
    var dictionary = new ActiveXObject("Scripting.Dictionary"); // VBA-compatible array 
    for (var i = 0; i < d.length; i++) { 
    dictionary.add(i, JS2VBArray(d[i])); 
    } 
    return dictionary.Items(); 
} 

function JS2VBArray(objJSArray) 
{ 
    var dictionary = new ActiveXObject("Scripting.Dictionary"); 
    for (var i = 0; i < objJSArray.length; i++) { 
     dictionary.add(i, objJSArray[ i ]); 
     } 
    return dictionary.Items(); 
} 

मैं WSC पंजीकृत है और यह ठीक काम किया। यह फोन करने के लिए VBA में कोड इस प्रकार है:

Public Function GetDiffs(ByVal s1 As String, ByVal s2 As String) As Variant() 
    Dim objWMIService As Object 
    Dim objDiff As Object 
    Set objWMIService = GetObject("winmgmts:") 
    Set objDiff = CreateObject("Google.DiffMatchPath.WSC") 
    GetDiffs = objDiff.DiffFast(s1, s2) 
    Set objDiff = Nothing 
    Set objWMIService = Nothing 
End Function 

(मैं एक ही वैश्विक objWMIService रखने की कोशिश की और objDiff तो चारों ओर मैं बना/प्रत्येक कक्ष के लिए इन नष्ट करने के लिए नहीं होगा, लेकिन यह नहीं मालूम था प्रदर्शन पर एक अंतर बनाने के लिए।)

मैंने फिर अपना मुख्य मैक्रो लिखा। इसमें तीन पैरामीटर होते हैं: मूल मानों की एक सीमा (एक कॉलम), नए मानों की एक श्रृंखला, और एक सीमा जहां भिन्नता को परिणामों को डंप करना चाहिए। सभी हैं पर समान संख्या में पंक्ति रखने के लिए, मुझे यहां कोई गंभीर त्रुटि-जांच नहीं चल रही है।

Public Sub DiffAndFormat(ByRef OriginalRange As Range, ByRef NewRange As Range, ByRef DeltaRange As Range) 
    Dim idiff As Long 
    Dim thisDiff() As Variant 
    Dim diffop As String 
    Dim difftext As String 
    difftext = "" 
    Dim diffs() As Variant 
    Dim OriginalValue As String 
    Dim NewValue As String 
    Dim DeltaCell As Range 
    Dim row As Integer 
    Dim CalcMode As Integer 

ये अगले तीन लाइनों बाद में उपयोगकर्ता की पसंदीदा गणना मोड botching बिना अद्यतन में तेजी लाने के:

Application.ScreenUpdating = False 
    CalcMode = Application.Calculation 
    Application.Calculation = xlCalculationManual 
    For row = 1 To OriginalRange.Rows.Count 
     difftext = "" 
     OriginalValue = OriginalRange.Cells(row, 1).Value 
     NewValue = NewRange.Cells(row, 1).Value 
     Set DeltaCell = DeltaRange.Cells(row, 1) 
     If OriginalValue = "" And NewValue = "" Then 

पिछले डिफ मिटाया जा रहा है, यदि कोई हो, के लिए महत्वपूर्ण है:

  Erase diffs 

यह परीक्षण मेरे उपयोगकर्ताओं के लिए एक दृश्य शॉर्टकट है, इसलिए जब कोई बदलाव नहीं होता है तो यह स्पष्ट होता है:

 ElseIf OriginalValue = NewValue Then 
      difftext = "No change." 
      Erase diffs 
     Else 

एक साथ सभी पाठ कम्बाइन डेल्टा सेल मूल्य के रूप में, चाहे पाठ समान था, डाला, या नष्ट कर दिया:

 DeltaCell.value2 = difftext 
     Call FormatDiff(diffs, DeltaCell) 
    Next 
    Application.ScreenUpdating = True 
    Application.Calculation = CalcMode 
End Sub 
:

  diffs = GetDiffs(OriginalValue, NewValue) 
      For idiff = 0 To UBound(diffs) 
       thisDiff = diffs(idiff) 
       difftext = difftext & thisDiff(1) 
      Next 
     End If 

आप मूल्य सेट करने के लिए स्वरूपण शुरू करने से पहले

Public Sub FormatDiff(ByRef diffs() As Variant, ByVal cell As Range) 
    Dim idiff As Long 
    Dim thisDiff() As Variant 
    Dim diffop As String 
    Dim difftext As String 
    cell.Font.Strikethrough = False 
    cell.Font.ColorIndex = 0 
    cell.Font.Bold = False 
    If Not diffs Then Exit Sub 
    Dim lastlen As Long 
    Dim thislen As Long 
    lastlen = 1 
    For idiff = 0 To UBound(diffs) 
     thisDiff = diffs(idiff) 
     diffop = thisDiff(0) 
     thislen = Len(thisDiff(1)) 
     Select Case diffop 
      Case -1 
       cell.Characters(lastlen, thislen).Font.Strikethrough = True 
       cell.Characters(lastlen, thislen).Font.ColorIndex = 16 ' Dark Gray http://www.microsoft.com/technet/scriptcenter/resources/officetips/mar05/tips0329.mspx 
      Case 1 
       cell.Characters(lastlen, thislen).Font.Bold = True 
       cell.Characters(lastlen, thislen).Font.ColorIndex = 32 ' Blue 
     End Select 
     lastlen = lastlen + thislen 
    Next 
End Sub 
:

यहाँ कोड है कि डिफ व्याख्या करती है और डेल्टा सेल स्वरूप है

अनुकूलन के लिए कुछ अवसर हैं, लेकिन अभी तक यह ठीक काम कर रहा है। जिसने भी मदद की उसका धन्यवाद!

+0

ठंडा। खुशी है कि यह आपके लिए कारगर रहा। भविष्य में, यदि आप चाहें तो आप अपने प्रश्न का उत्तर दे सकते हैं। यह एक नीले टेक्स्टबॉक्स में पॉप अप होगा; दृष्टि से यह स्पष्ट है कि आपने इसे पोस्ट किया है। – Cheeso

+0

Google diff/merge/पैच प्रोजेक्ट में अब एक (पूरी तरह प्रबंधित) सी # पोर्ट शामिल है। –

उत्तर

11

जावास्क्रिप्ट का उपयोग कर सीधे COM घटक में जावास्क्रिप्ट diff तर्क को एम्बेड करने का सबसे आसान तरीका हो सकता है। यह "Windows Script Components" नामक किसी चीज़ के माध्यम से संभव है।

यहां a tutorial on creating WSCs है।

एक विंडोज स्क्रिप्ट घटक एक COM घटक है जिसे स्क्रिप्ट में परिभाषित किया गया है। घटक के लिए इंटरफ़ेस COM के माध्यम से है, जिसका अर्थ है कि यह वीबीए अनुकूल है। तर्क किसी भी विंडोज स्क्रिप्टिंग होस्टिंग-संगत भाषा, जैसे जावास्क्रिप्ट या वीबीस्क्रिप्ट में लागू किया गया है। डब्लूएससी को एक एक्सएमएल फ़ाइल में परिभाषित किया गया है, जो तर्क, घटक वर्ग आईडी, विधियों, पंजीकरण तर्क आदि को एम्बेड करता है।

tool available to help in creating a WSC भी है। असल में यह एक जादूगर-प्रकार की चीज है जो आपको प्रश्न पूछती है और एक्सएमएल टेम्पलेट में भरती है। स्वयं, मैंने बस एक उदाहरण .wsc फ़ाइल के साथ शुरू किया और इसे टेक्स्ट एडिटर के साथ हाथ से संपादित किया। यह बहुत आत्म-व्याख्यात्मक है।

स्क्रिप्ट में (एक .wsc फ़ाइल में) इस तरह से परिभाषित एक COM घटक किसी भी अन्य COM घटक की तरह कॉल करने योग्य है, जो COM के साथ नृत्य कर सकता है।

अद्यतन: मैंने कुछ मिनट लिया और GoogleDiff के लिए WSC का उत्पादन किया। यही पर है।

<?xml version="1.0"?> 

<package> 

<component id="Cheeso.Google.DiffMatchPatch"> 

    <comment> 
    COM Wrapper on the Diff/Match/Patch logic published by Google at http://code.google.com/p/google-diff-match-patch/. 
    </comment> 

<?component error="true" debug="true"?> 

<registration 
    description="WSC Component for Google Diff/Match/Patch" 
    progid="Cheeso.Google.DiffMatchPatch" 
    version="1.00" 
    classid="{36e400d0-32f7-4778-a521-2a5e1dd7d11c}" 
    remotable="False"> 

    <script language="VBScript"> 
    <![CDATA[ 

    strComponent = "Cheeso's COM wrapper for Google Diff/Match/Patch" 

    Function Register 
     MsgBox strComponent & " - registered." 
    End Function 

    Function Unregister 
     MsgBox strComponent & " - unregistered." 
    End Function 

    ]]> 
    </script> 
</registration> 


<public> 
    <method name="Diff"> 
    <parameter name="text1"/> 
    <parameter name="text2"/> 
    </method> 
    <method name="DiffFast"> 
    <parameter name="text1"/> 
    <parameter name="text2"/> 
    </method> 
</public> 


<script language="Javascript"> 
<![CDATA[ 


    // insert original google diff code here... 


// public methods on the component 
var dpm = new diff_match_patch(); 


function Diff(text1, text2) 
{ 
    return dpm.diff_main(text1, text2, false); 
} 


function DiffFast(text1, text2) 
{ 
    return dpm.diff_main(text1, text2, true); 
} 


]]> 
</script> 

</component> 

</package> 

उस चीज़ का उपयोग करने के लिए, आपको इसे पंजीकृत करना होगा। एक्सप्लोरर में, राइट क्लिक करें, और "रजिस्टर" का चयन करें। या, कमांड लाइन से: regsvr32 फ़ाइल: \ c: \ scripts \ GoogleDiff.wsc

मैंने इसे VBA से उपयोग करने का प्रयास नहीं किया, लेकिन यहां कुछ VBScript कोड है जो घटक का उपयोग करता है।

Sub TestDiff() 
    dim t1 
    t1 = "The quick brown fox jumped over the lazy dog." 

    dim t2 
    t2 = "The large fat elephant jumped over the cowering flea." 

    WScript.echo("") 

    WScript.echo("Instantiating a Diff Component ...") 
    dim d 
    set d = WScript.CreateObject("Cheeso.Google.DiffMatchPatch") 

    WScript.echo("Doing the Diff...") 
    x = d.Diff(t1, t2) 

    WScript.echo("") 
    WScript.echo("Result was of type: " & TypeName(x)) 
    ' result is all the diffs, joined by commas. 
    ' Each diff is an integer (position), and a string. These are separated by commas. 
    WScript.echo("Result : " & x) 

    WScript.echo("Transform result...") 
    z= Split(x, ",") 
    WScript.echo("") 
    redim diffs(ubound(z)/2) 
    i = 0 
    j = 0 
    For Each item in z 
     If (j = 0) then 
     diffs(i) = item 
     j = j+ 1  
     Else 
      diffs(i) = diffs(i) & "," & item 
     i = i + 1 
     j = 0 
     End If 
    Next 

    WScript.echo("Results:") 
    For Each item in diffs 
     WScript.echo(" " & item) 
    Next 

    WScript.echo("Done.") 

End Sub 
+0

बहुत बढ़िया। जब मुझे मौका मिलता है तो मैं इसे एक शॉट देने जा रहा हूं। इस बीच, मैं इसे सबसे अच्छा जवाब के रूप में स्वीकार करूंगा। – richardtallent

+0

बंद करें, लेकिन diff_main भिन्नता की एक सरणी देता है, जिनमें से प्रत्येक ऑपरेटर (बराबर, हटाया गया, या डाला गया, एक पूर्णांक के रूप में) और पाठ के साथ दो-तत्व सरणी है। मैं अभी भी एक एरे के रूप में परिणाम का इलाज करने के लिए वीबीए कैसे प्राप्त कर सकता हूं, इस माध्यम से काम कर रहा हूं ताकि मैं इसके माध्यम से कदम उठा सकूं और एक्सेल सेल में उचित प्रारूपण कर सकूं। – richardtallent

+0

मुझे पता है कि जावास्क्रिप्ट तर्क क्या है कि वापसी मूल्य है। मेरे वीबीस्क्रिप्ट परीक्षण में, वापसी मूल्य का प्रकार स्ट्रिंग है। तो मेरे उदाहरण vbscript में, मैंने स्ट्रिंग को विभाजित किया और "अंतर" की सरणी को फिर से बनाया। – Cheeso

2

मेरा सुझाव यह होगा कि आप जो कुछ भी करते हैं, उसे COM wrapper में लपेटें। वीबीए COM ऑब्जेक्ट्स के साथ सबसे अच्छा काम करता है ताकि आप .NET घटक के रूप में संकलित कर सकें, फिर .NET की इंटरऑप कार्यक्षमता का उपयोग करके COM ऑब्जेक्ट के रूप में एक्सपोज़ करें।

एक विकल्प के रूप में आप जावास्क्रिप्ट फ़ाइल निष्पादित करने और परिणाम देने के लिए विंडोज स्क्रिप्टिंग होस्ट ऑब्जेक्ट्स का उपयोग करने में भी देख सकते हैं।

+0

आप दोनों कर सकते हैं। विंडोज स्क्रिप्ट घटक का उपयोग करके आप जावास्क्रिप्ट में अपने COM घटक को परिभाषित कर सकते हैं, और वीबीए या जो कुछ भी से COM घटक का आह्वान कर सकते हैं। – Cheeso

4

Windows Scripting Engine आपको जावास्क्रिप्ट लाइब्रेरी चलाने की अनुमति देगा। यह मेरे अनुभव में अच्छी तरह से काम करता है।

+0

और जावा घटक के रूप में जावास्क्रिप्ट तर्क पैकेजिंग करके, इस बात के माध्यम से माइक्रोसॉफ्ट विंडोज स्क्रिप्ट घटक कहते हैं, एक्सेल/वीबीए से जावास्क्रिप्ट को कॉल करना आसान होगा। – Cheeso

1

यहां विचार करने का एक और विकल्प है, हालांकि मैं किसी भी माध्यम से इसका सर्वोत्तम वर्णन नहीं कर रहा हूं।

  • बीमा करें कि पाइथन संस्करण आयरनपीथन में संकलित करता है। (यहां कोई समस्या नहीं होनी चाहिए, या अधिकतर पोर्टिंग की केवल थोड़ी सी मात्रा नहीं होनी चाहिए।)
  • सी # का उपयोग करके एक्सेल ऐड-इन लाइब्रेरी बनाएं और इसके द्वारा आयरनपीथन संदर्भ दें।
  • अपने सी # एक्सेल एड-इन में आवश्यक कार्यक्षमता लपेटें।
+0

कि एक _all .Net_ समाधान मिलेगा। मुझें यह पसंद है। –

+0

मुझे एक सभी .NET समाधान पसंद आएगा, लेकिन मैं एक्सेल 2003 के साथ फंस गया हूं। साथ ही, मेरे उपयोगकर्ताओं को .NET रनटाइम का एक विशेष संस्करण स्थापित हो सकता है या नहीं, इसलिए एक ऑल-वीबीए समाधान पसंद किया जाता है। – richardtallent

+0

उस स्थिति में (COM आधारित) विंडोज स्क्रिप्टिंग इंजन बिल को फिट करना चाहिए। –

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

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