Wie kann ich JavaScript in einem Excel-Makro?
-
21-08-2019 - |
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!
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.