Frage

Es gibt eine wirklich coole diff Klasse von Google gehostet hier:

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

Ich habe es schon auf einigen Web-Site verwendet, aber jetzt brauche ich es verwenden, um innerhalb ein Excel-Makro Text zwischen zwei Zellen zu vergleichen.

Es ist jedoch nur in JavaScript, Python, Java und C ++, nicht VBA.

Meine Benutzer sind Excel 2003 begrenzt, so dass eine reine .NET-Lösung nicht funktionieren würde. den Code VBA übersetzt manuell würde zu viel Zeit in Anspruch nehmen und schwierig zu aktualisieren.

Eine Option Ich war als der JavaScript oder Java-Quellcode mit den .NET-Compiler zu kompilieren (JScript.NET oder J #) verwendet Reflektor zur Ausgabe als VB.NET, dann degradieren schließlich den VB.NET-Code manuell zu VBA, geben mir eine reine VBA Lösung. Nachdem Probleme bekommen es mit einer beliebigen .NET-Compiler zu kompilieren, gab ich diesen Weg.

Angenommen, ich eine funktionierende NET-Bibliothek bekommen haben könnte, die ich verwendet habe könnte auch ExcelDna ( http: // www. codeplex.com/exceldna ), ein Open-Source-Excel-Add-in zu .NET-Code Integration erleichtern.

Meine letzte Idee war, ein Internet Explorer-Objekt zu bewirten, ihm die JavaScript-Quelle senden, und nennt es. Auch wenn ich diese Arbeit bekomme, meine Vermutung ist, wäre es schmutz langsam und chaotisch sein.

UPDATE: Lösung gefunden

Ich benutzte die WSC-Methode unten durch die akzeptierte Antwort beschrieben. Ich hatte den WSC-Code zu ändern, um ein wenig, um die diffs aufzuzuräumen und gib mir einen VBA-kompatible Array von Arrays zurück:

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

ich registriert die WSC und es funktionierte gut. Der Code in VBA für es Aufruf lautet wie folgt:

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

(Ich habe versucht, eine einzige globale objWMIService und objDiff zu halten um so hätte ich nicht erstellen / zerstören diese für jede Zelle, aber es schien keinen Unterschied auf der Leistung zu machen.)

Ich schrieb dann mein Haupt Makro. Es nimmt drei Parameter: einen Bereich (eine Spalte) der ursprünglichen Werte, eine Reihe von neuen Werten, und ein Bereich, in dem der Unterschied sollte gibt die Ergebnisse aus. Alle sind angenommen die gleiche Anzahl von Reihe zu haben, ich habe keine schwerwiegenden Fehler-Überprüfung geht hier vor.

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

Diese nächsten drei Zeilen beschleunigen das Update, ohne später botching den bevorzugten Berechnungsmodus des Benutzers:

    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

Löschen der vorherigen diffs, falls vorhanden, ist wichtig:

            Erase diffs

Dieser Test ist eine visuelle Abkürzung für meine Nutzer so ist es klar, wenn keine Änderung gibt es überhaupt:

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

Kombinieren Sie den gesamten Text zusammen als die Delta-Zellenwert, ob der Text war identisch, eingefügt oder gelöscht:

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

Sie haben den Wert auf vor Start der Formatierung:

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

Hier ist der Code, der die Differentiale und formatiert die Delta-Zelle interpretiert:

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

Es gibt einige Möglichkeiten zur Optimierung, aber so weit es funktioniert ganz gut. Vielen Dank an alle, die geholfen haben!

War es hilfreich?

Lösung

Der einfachste Ansatz kann die Javascript diff Logik in eine COM-Komponente Javascript direkt mit einbinden. Dies ist möglich, über die so genannte „ Windows Script Components “.

Hier ein Tutorial zum Erstellen von WSCs .

Ein Windows Script Component ist eine COM-Komponente, die in Skript definiert ist. Die Schnittstelle zur Komponente ist über COM, das heißt, es VBA freundlich. Die Logik ist in jeder Windows-Scripting-Hosting -kompatible Sprache, wie JavaScript oder VBScript implementiert. Die WSC wird in einer einzelnen XML-Datei definiert, die die Logik einbettet, um die Komponente der Klasse ID, die Methoden, die Registrierung Logik, und so weiter.

Es gibt auch ein Werkzeug zur Verfügung helfen, eine WSC zu schaffen. Im Grunde ist es ein Assistent-Typ, was Sie Fragen und füllt die XML-Vorlage fragt. Ich selbst mit einem Beispiel WSC-Datei gerade erst begonnen und bearbeiten es mit der Hand mit einem Texteditor. Es ist ziemlich selbsterklärend.

Eine COM-Komponente auf diese Weise in Skript definiert (in einer WSC-Datei) ist aufrufbar wie jede andere COM-Komponente, aus jeder Umgebung, die mit COM tanzen kann.

UPDATE : Ich brauchte ein paar Minuten und produzierte die WSC für GoogleDiff. Hier ist es.

<?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>

das Ding verwenden, müssen Sie es registrieren. Im Explorer mit der rechten darauf klicken, und wählen Sie „Register“. oder aus der Befehlszeile:     regsvr32 file: \ c: \ scripts \ GoogleDiff.wsc

Ich habe nicht versucht, es von VBA, aber hier ist einige VBScript-Code, der die Komponente verwendet.

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

Andere Tipps

Die Windows Scripting Motor können Sie die JavaScript-Bibliothek laufen. Es funktioniert gut in meiner Erfahrung.

Mein Vorschlag wäre, dass alles, was Sie tun Sie es wickeln in einem COM-Wrapper. VBA befasst sich am besten mit COM-Objekten, so dass Sie als eine .NET-Komponente dann als COM-Objekt mit der Interop-Funktionalität von .NET aussetzen kompilieren können.

Als Alternative Sie auch in mit Windows Scripting Host-Objekten aussehen könnten eine JavaScript-Datei auszuführen und das Ergebnis zurück.

Hier ist eine andere Option zu prüfen, obwohl ich nicht mit irgendwelchen Mitteln bin besagt es das Beste.

  • Sie sicher, dass die Python-Version in Ironpython kompiliert. (Es sollte keine Probleme geben hier, oder nur eine geringe Menge an den meisten portieren.)
  • Erstellen Sie eine Excel-Add-in-Bibliothek mit C # und Referenzironpython von ihm.
  • Wickeln Sie die notwendige Funktionalität in C # Excel-Add-in.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top