質問

Google が主催する本当に素晴らしい diff クラスがここにあります。

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

以前にいくつかの Web サイトで使用しましたが、今は使用する必要があります 内で 2 つのセル間のテキストを比較する Excel マクロ。

ただし、これは JavaScript、Python、Java、および C++ でのみ使用でき、VBA では使用できません。

私のユーザーは Excel 2003 に限定されているため、純粋な .NET ソリューションは機能しません。コードを手動で VBA に変換すると、時間がかかりすぎてアップグレードが困難になります。

私が検討したオプションの 1 つは、.NET コンパイラ (JScript.NET または J#) を使用して JavaScript または Java ソースをコンパイルし、Reflector を使用して VB.NET として出力し、最後に VB.NET コードを手動で VBA にダウングレードして、純粋なVBA ソリューション。どの .NET コンパイラでもコンパイルできないという問題があったため、このパスを放棄しました。

動作する .NET ライブラリを入手できたと仮定すると、ExcelDna も使用できたでしょう (http://www.codeplex.com/exceldna)、.NET コードの統合を容易にするオープンソース Excel アドイン。

私の最後のアイデアは、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 を保持しようとしましたが、パフォーマンスに違いはないようです。)

次に、メインのマクロを作成しました。次の 3 つのパラメータを取ります。元の値の範囲 (1 列)、新しい値の範囲、および 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

次の 3 行は、ユーザーが選択した計算モードを後で失敗することなく、更新を高速化します。

    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

テキストが同一であるか、挿入されたか、削除されたかに関係なく、すべてのテキストをデルタ セル値として結合します。

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

値を設定する必要があります 前に フォーマットを開始します:

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

diff を解釈してデルタ セルをフォーマットするコードは次のとおりです。

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

最適化の余地はいくつかありますが、今のところ問題なく動作しています。助けてくれたみんなに感謝します!

役に立ちましたか?

解決

最も簡単な方法は、JavaScript を使用して Javascript diff ロジックを COM コンポーネントに直接埋め込むことです。これは、「」と呼ばれるものを通じて可能です。Windows スクリプト コンポーネント".

こちらです WSC の作成に関するチュートリアル.

Windows スクリプト コンポーネントは、スクリプトで定義される COM コンポーネントです。コンポーネントへのインターフェイスは COM 経由であるため、VBA に対応しています。ロジックは、JavaScript や VBScript などの Windows スクリプト ホスティングと互換性のある言語で実装されます。WSC は、ロジック、コンポーネントのクラス ID、メソッド、登録ロジックなどが埋め込まれた単一の XML ファイルで定義されます。

もあります WSC の作成に役立つツールが利用可能. 。基本的には、質問をして XML テンプレートに入力するウィザード形式のものです。私自身は、サンプルの .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

他のヒント

Windows スクリプト エンジン JavaScript ライブラリを実行できるようになります。私の経験ではそれはうまくいきます。

私の提案は、何をするにしても、それを COM ラッパーでラップすることです。VBA は COM オブジェクトを最もよく扱うため、.NET コンポーネントとしてコンパイルし、.NET の相互運用機能を使用して COM オブジェクトとして公開できます。

別の方法として、Windows Scripting Host オブジェクトを使用して Javascript ファイルを実行し、結果を返すことも検討できます。

検討すべきもう 1 つのオプションがありますが、それが最善であるとは決して言いません。

  • Python のバージョンが IronPython でコンパイルされることを確認してください。(ここでは問題は発生しないか、せいぜい少量の移植だけであるはずです。)
  • C# を使用して Excel アドイン ライブラリを作成し、そこから IronPython を参照します。
  • 必要な機能を C# Excel アドインにラップします。
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top