2009-05-11 35 views
16

Có một lớp khác thực sự mát mẻ lưu trữ bởi Google ở ​​đây:Tôi làm cách nào để sử dụng JavaScript trong macro Excel?

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

Tôi đã sử dụng nó trước khi vào một vài trang web, nhưng bây giờ tôi cần phải sử dụng nó trong một macro Excel để so sánh văn bản giữa hai ô.

Tuy nhiên, nó chỉ có sẵn trong JavaScript, Python, Java và C++ chứ không phải VBA.

Người dùng của tôi bị giới hạn trong Excel 2003, do đó, một giải pháp .NET thuần túy sẽ không hoạt động. Việc dịch mã sang VBA theo cách thủ công sẽ mất quá nhiều thời gian và làm cho việc nâng cấp trở nên khó khăn.

Một tùy chọn tôi xem là biên dịch mã nguồn JavaScript hoặc Java bằng cách sử dụng trình biên dịch .NET (JScript.NET hoặc J #), sử dụng Reflector để xuất dưới dạng VB.NET, sau đó cuối cùng hạ cấp mã VB.NET theo cách thủ công thành VBA, cho tôi một giải pháp VBA thuần túy. Sau khi có vấn đề nhận được nó để biên dịch với bất kỳ trình biên dịch .NET, tôi bỏ qua con đường này.

Giả sử tôi có thể đã nhận được thư viện .NET hoạt động, tôi cũng có thể đã sử dụng ExcelDna (http://www.codeplex.com/exceldna), một phần bổ trợ Excel nguồn mở để làm cho tích hợp mã .NET dễ dàng hơn.

Ý tưởng cuối cùng của tôi là lưu trữ một đối tượng Internet Explorer, gửi cho nó nguồn JavaScript và gọi nó. Ngay cả khi tôi làm việc này, tôi đoán là nó sẽ rất chậm và lộn xộn.

CẬP NHẬT: Tìm thấy giải pháp!

Tôi đã sử dụng phương pháp WSC được mô tả bên dưới bằng câu trả lời được chấp nhận. Tôi đã phải thay đổi mã WSC một chút để dọn dẹp diffs và đưa tôi trở lại một mảng VBA tương thích của mảng:

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

Tôi đã đăng ký WSC và nó chỉ làm việc tốt. Các mã trong VBA để gọi nó là như sau:

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 

(Tôi cố gắng giữ một objWMIService toàn cầu duy nhất và objDiff xung quanh vì vậy tôi sẽ không phải tạo/phá hủy những cho mỗi tế bào, nhưng nó không có vẻ để tạo sự khác biệt về hiệu suất.)

Sau đó tôi đã viết macro chính của mình. Nó có ba tham số: một phạm vi (một cột) của các giá trị ban đầu, một loạt các giá trị mới, và một phạm vi nơi khác biệt nên đổ kết quả. Tất cả là giả định để có cùng số hàng, tôi không có bất kỳ kiểm tra lỗi nghiêm trọng nào xảy ra tại đây.

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 

Ba dòng tiếp theo tăng tốc độ cập nhật mà không botching chế độ tính ưa thích của người dùng sau:

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 

Tẩy xoá diffs trước, nếu có, là rất quan trọng:

  Erase diffs 

này kiểm tra là lối tắt trực quan cho người dùng của tôi để rõ ràng khi không có thay đổi gì cả:

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

Kết hợp tất cả các văn bản với nhau như những giá trị của ô tam giác, cho dù văn bản là giống hệt nhau, chèn, hoặc xóa:

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

Bạn phải thiết lập giá trị trước bắt đầu định dạng:

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

Đây là mã giải thích các khác biệt và định dạng ô delta:

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 

Có một số cơ hội để tối ưu hóa, nhưng cho đến nay nó vẫn hoạt động tốt. Cảm ơn mọi người đã giúp đỡ!

+0

. Vui mừng nó đã làm việc cho bạn. Trong tương lai, nếu bạn thích bạn có thể trả lời câu hỏi của riêng bạn. Nó sẽ bật lên trong một hộp văn bản màu xanh; trực quan rõ ràng là bạn đã đăng nó. – Cheeso

+0

Dự án Google diff/merge/patch hiện bao gồm một cổng C# (được quản lý hoàn toàn). –

Trả lời

11

Cách tiếp cận đơn giản nhất có thể là nhúng logic khác Javascript vào thành phần COM trực tiếp bằng Javascript. Điều này có thể thông qua một cái gì đó gọi là "Windows Script Components".

Đây là a tutorial on creating WSCs.

Thành phần Windows Script là thành phần COM được xác định trong tập lệnh. Giao diện cho các thành phần là thông qua COM, có nghĩa là nó là VBA thân thiện. Logic được thực hiện trong bất kỳ ngôn ngữ tương thích với Windows Scripting Hosting nào, như JavaScript hoặc VBScript. WSC được định nghĩa trong một tệp XML duy nhất, nó nhúng logic, thành phần Class ID, các phương thức, logic đăng ký, v.v.

Ngoài ra còn có tool available to help in creating a WSC. Về cơ bản nó là một loại trình thủ thuật hỏi bạn những câu hỏi và điền vào mẫu XML. Bản thân tôi, tôi chỉ bắt đầu với một tệp .wsc mẫu và chỉnh sửa nó bằng tay với một trình soạn thảo văn bản. Nó khá tự giải thích.

Thành phần COM được định nghĩa theo cách này trong tập lệnh (trong tệp .wsc) có thể gọi giống như bất kỳ thành phần COM nào khác, từ bất kỳ môi trường nào có thể nhảy với COM.

CẬP NHẬT: Tôi mất vài phút và tạo WSC cho GoogleDiff. Đây rồi.

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

Để sử dụng điều đó, bạn phải đăng ký. Trong Explorer, nhấp chuột phải vào nó và chọn "Đăng ký". hoặc, từ dòng lệnh: regsvr32 file: \ c: \ scripts \ GoogleDiff.wsc

Tôi đã không thử sử dụng nó từ VBA, nhưng đây là một số mã VBScript sử dụng thành phần.

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

Tuyệt vời. Tôi sẽ cung cấp cho một shot này khi tôi có cơ hội. Trong khi chờ đợi, tôi sẽ chấp nhận điều này như là câu trả lời hay nhất. – richardtallent

+0

Đóng, nhưng diff_main trả về một mảng các khác biệt, mỗi mảng là một mảng gồm hai phần tử với toán tử (bằng, xóa hoặc chèn, dưới dạng số nguyên) và văn bản. Tôi vẫn làm việc thông qua làm thế nào để có được VBA để điều trị kết quả như là một mảng để tôi có thể bước qua nó và tạo ra các định dạng thích hợp trong các tế bào Excel. – richardtallent

+0

Tôi biết logic javascript THINKS giá trị trả về là gì. Trong thử nghiệm VBScript của tôi, kiểu giá trị trả về là String. Vì vậy, trong vbscript ví dụ của tôi, tôi chia chuỗi và xây dựng lại mảng "sự khác biệt". – Cheeso

2

Đề xuất của tôi sẽ là bất cứ điều gì bạn làm bạn quấn nó trong trình bao bọc COM. VBA thỏa thuận tốt nhất với các đối tượng COM, do đó bạn có thể biên dịch thành một thành phần .NET sau đó trưng ra như một đối tượng COM sử dụng chức năng interop của .NET.

Thay vào đó, bạn cũng có thể xem xét sử dụng các đối tượng Máy chủ Windows Scripting để thực thi Tệp Javascript và trả về kết quả.

+0

Bạn có thể làm cả hai. Sử dụng các thành phần Windows Script bạn có thể định nghĩa thành phần COM của bạn trong Javascript, và gọi thành phần COM từ VBA hoặc bất cứ thứ gì. – Cheeso

4

Windows Scripting Engine sẽ cho phép bạn chạy thư viện JavaScript. Nó hoạt động tốt trong kinh nghiệm của tôi.

+0

Và bằng cách đóng gói các logic Javascript như là một thành phần COM, thông qua điều này Microsoft gọi Windows Script Components, nó sẽ được dễ dàng để gọi Javascript từ Excel/VBA. – Cheeso

1

Đây là một tùy chọn khác để xem xét, mặc dù tôi không phải bằng bất kỳ phương tiện nào nêu rõ điều tốt nhất của nó.

  • Đảm bảo phiên bản Python biên dịch trong IronPython. (Không nên có bất kỳ vấn đề nào ở đây, hoặc chỉ một lượng nhỏ chuyển đổi nhiều nhất.)
  • Tạo một thư viện bổ trợ Excel bằng C# và tham chiếu IronPython từ nó.
  • Bao gồm các chức năng cần thiết trong bổ trợ C# Excel của bạn.
+0

Điều đó sẽ nhận được giải pháp _all .Net_. Tôi thích nó. –

+0

Tôi rất thích giải pháp all- .NET, NHƯNG tôi bị mắc kẹt với Excel 2003. Ngoài ra, người dùng của tôi có thể hoặc không có phiên bản .NET runtime cụ thể được cài đặt, do đó, giải pháp tất cả VBA được ưu tiên. – richardtallent

+0

Trong trường hợp đó, Windows Scripting Engine (COM) phải phù hợp với hóa đơn. –

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