2009-05-11 7 views
16

여기 Google에서 호스팅 정말 멋진 사랑하는 클래스있다 :Excel 매크로에서 JavaScript를 사용하려면 어떻게해야합니까?

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

나는 몇 가지 웹 사이트에 전에 그것을 사용했지만, 지금은 텍스트를 비교하는 엑셀 매크로 내에서 그것을 를 사용할 필요가 두 세포 사이.

그러나 VBA가 아닌 JavaScript, Python, Java 및 C++에서만 사용할 수 있습니다.

내 사용자는 Excel 2003으로 제한되어 있으므로 순수한 .NET 솔루션이 작동하지 않습니다. 코드를 수동으로 VBA로 변환하는 것은 너무 많은 시간이 걸리고 업그레이드를 어렵게 만듭니다.

.NET 컴파일러 (JScript.NET 또는 J #)를 사용하여 JavaScript 또는 Java 소스를 컴파일하고 Reflector를 사용하여 VB.NET으로 출력 한 다음 VB.NET 코드를 수동으로 VBA로 다운 그레이드하는 옵션을 고려했습니다. 나에게 순수한 VBA 솔루션을 제공합니다. 모든 .NET 컴파일러로 컴파일 할 때 문제가 발생한 후이 경로를 포기했습니다.

내가 작동하는 .NET 라이브러리를 얻었다면, ExcelDna (http://www.codeplex.com/exceldna), 오픈 소스 Excel 추가 기능을 사용하여 .NET 코드 통합을 더 쉽게 할 수있었습니다.

마지막으로 Internet Explorer 개체를 호스팅하고 JavaScript 원본을 보내고 호출하는 것이 좋습니다. 내가 이것을 작동 시키더라도 내 추측은 더러움이 느리고 지저분 할 것입니다.

업데이트 : 해결책을 찾았습니다!

아래에 설명 된 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 주위에 내가 생성/각 셀이를 파괴 할 필요가 없습니다 것입니다, 그러나 보이지 않았다 성능에 차이를 만들 수 있습니다.)

그런 다음 내 주 매크로를 작성했습니다. 세 가지 매개 변수를 사용합니다 : 원래 값의 범위 (한 열), 새 값의 범위 및 diff가 결과를 덤프해야하는 범위. 모두 이 행의 동일한 번호를 갖는으로 가정하고 있습니다. 여기에 심각한 오류 검사가 없습니다.

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 

이 다음 세 줄

나중에 사용자가 선호하는 계산 모드를 서투르게 수선없이 업데이트를 속도 :

  Erase diffs 

이를 :

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 

이있는 경우, 중요하다, 이전의 차이점을 지우기 테스트는 내 사용자를위한 시각적 인 바로 가기이므로 전혀 변경 사항이 없을 때 명확합니다.

,

텍스트, 동일했다 삽입 또는 삭제 여부, 델타 셀 값으로 함께 모든 텍스트를 결합 :

 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

cool. 다행이 당신을 위해 일 했어. 미래에, 당신이 원한다면 당신은 당신 자신의 질문에 대답 할 수 있습니다. 파란색 텍스트 상자에 팝업됩니다. 시각적으로 당신이 게시했음을 분명히합니다. – Cheeso

+0

Google diff/merge/patch 프로젝트에 (완전히 관리되는) C# 포트가 포함되었습니다. –

답변

11

가장 간단한 방법은 Javascript를 사용하여 COM 구성 요소에 Javascript diff 논리를 직접 포함시키는 것입니다. 이것은 "Windows Script Components"을 통해 가능합니다.

여기에 a tutorial on creating WSCs입니다.

Windows 스크립트 구성 요소는 스크립트에 정의 된 COM 구성 요소입니다. 구성 요소에 대한 인터페이스는 VBA와 호환되는 COM을 통해 이루어집니다. 이 논리는 JavaScript 또는 VBScript와 같은 모든 Windows Scripting Hosting 호환 언어로 구현됩니다. WSC는 논리, 구성 요소 클래스 ID, 메서드, 등록 논리 등을 포함하는 단일 XML 파일에 정의됩니다.

tool available to help in creating a WSC도 있습니다. 기본적으로 XML 템플릿을 질문하고 채우는 마법사 형식의 개체입니다. 저는 예제 .wsc 파일로 시작하여 텍스트 편집기로 직접 편집했습니다. 그것은 꽤 자명합니다.

스크립트에서 이런 식으로 정의 된 COM 구성 요소 (.wsc 파일)는 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> 

그 것을 사용하려면 등록해야합니다. Explorer에서 마우스 오른쪽 버튼을 클릭하고 "Register"를 선택하십시오. 또는 명령 줄에서 다음을 입력하십시오. 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은 각 연산자가 (같음, 삭제 또는 정수로 삽입 된) 연산자와 텍스트의 두 요소 배열 인 차이의 배열을 반환합니다. 나는 VBA에서 결과를 배열로 처리하여 Excel 셀에서 적절한 형식을 만들 수 있도록하는 방법을 계속 연구 중이다. – richardtallent

+0

자바 스크립트 로직이 반환 값을 알고 있다고 생각합니다. 내 VBScript 테스트에서 반환 값의 유형은 String입니다. 그래서 내 예제 VBScript에서 문자열을 분리하고 "차이"의 배열을 다시 작성했습니다. – Cheeso

2

내 제안은 무엇이든지간에 COM 래퍼로 래핑하는 것입니다. VBA는 COM 개체를 가장 잘 처리하므로 .NET 구성 요소로 컴파일 한 다음 .NET의 interop 기능을 사용하여 COM 개체로 표시 할 수 있습니다.

대신 Windows Scripting Host 개체를 사용하여 Javascript 파일을 실행하고 결과를 반환 할 수도 있습니다.

+0

둘 다 할 수 있습니다. Windows 스크립트 구성 요소를 사용하면 Javascript에서 COM 구성 요소를 정의하고 VBA 등에서 COM 구성 요소를 호출 할 수 있습니다. – Cheeso

4

Windows Scripting Engine을 사용하면 JavaScript 라이브러리를 실행할 수 있습니다. 그것은 내 경험에서 잘 작동합니다.

+0

그리고 Javascript 논리를 COM 구성 요소로 패키징하면 Microsoft에서 Windows Script Components를 호출하여 Excel/VBA에서 Javascript를 쉽게 호출 할 수 있습니다. – Cheeso

1

다른 옵션을 고려해 볼 수 있습니다. 단연 가장 좋은 것은 아닙니다.

  • IronPython에서 Python 버전이 컴파일되도록하십시오. 여기에는 문제가 없거나 최소한의 포팅 만 수행해야합니다.
  • C# 및 참조 IronPython을 사용하여 Excel 추가 기능 라이브러리를 만듭니다.
  • C# Excel 추가 기능에서 필요한 기능을 줄 바꿈하십시오.
+0

그건 _all .Net_ 솔루션을 얻을 것이다. 나는 그것을 좋아한다. –

+0

나는 all -.NET 솔루션을 좋아하지만 Excel 2003이 남아 있습니다. 또한 사용자에게 .NET 런타임의 특정 버전이 설치되어있을 수도 있고 없을 수도 있습니다. 따라서 모든 VBA 솔루션이 선호됩니다. – richardtallent

+0

그런 경우 (COM 기반) Windows 스크립팅 엔진이 청구서에 적합해야합니다. –

관련 문제