2013年10月14日月曜日

サクラエディタ用Grep&置換マクロ(full)

サクラエディタのGrep結果を基に置換を行うVBSのマクロです。
以前公開していたものはそれ単品では動作しない形式だったので、
必要な関数なども併せてフルバージョンとして置いておきます。
個人的に作成・利用をしているマクロで十分なデバッグが行われているとは言えません。
また処理対象を直接編集・保存している為、処理後に元に戻すことができません。
こういった点を理解した上、自己責任での利用をお願いします。

GrepAndReplace.vbs
'Grep結果を基に置換を行うマクロ
'
'【詳細】
'サクラエディタのGrep結果を基に文字列の置換を行います。
'置換の対象はGrep結果に載っているファイル&行だけです。
'置換文字列の指定はGrep結果の1行目に下記のフォーマットで記入してください。
'置換前後の文字列に「タブ」や「改行」は指定できません。
'【フォーマット】
'置換前文字列[tab]置換後文字列
'【例】"あいうえお"を"かきくけこ"に置換する場合
'あいうえお    かきくけこ
'---------------------------------------------------------------------
'サクラエディタの検索パラメータ
Const OPT_WORD = 1    '単語単位で探す(W)
Const OPT_DIFF = 2    '英大文字と小文字を区別する(C)
Const OPT_RE = 4    '正規表現(E)
Const OPT_MSG = 8    '見つからないときにメッセージを表示(M)
Const OPT_DCLS = 16    '検索ダイアログを自動的に閉じる(L)
Const OPT_LOOP = 32    '先頭(末尾)から再検索する(Z)
Const OPT_CB = 64    'クリップボードから貼り付ける(T)
Const OPT_TGT = 128    '0:ファイル全体(O),1:選択範囲(S)

'置換処理を行うStrategy
'Publicで宣言されている部分の処理を差し替えることで、
'Grep結果に対する様々な処理を実装することができます。
Class Strategy

    Private lngSize
    Private strBefore
    Private strAfter
    Private lngOption
    Private strPreviousPath
    Private strErrMessage

    'コンストラクタ
    Private Sub Class_Initialize()
    End Sub

    'デストラクタ
    Private Sub Class_Terminate()
    End Sub

    '初期化処理
    Public Function initialize()
        initialize = False
       
        '1:選択範囲(S) + '検索ダイアログを自動的に閉じる(L) + '英大文字と小文字を区別する(C)
        lngOption = OPT_TGT + OPT_DCLS + OPT_DIFF
        lngSize = 0
        strPreviousPath = ""
        strErrMessage = ""

        Dim strReplaceStringArray
        strReplaceStringArray = Split(getString(Editor.GetLineStr(1)), vbTab)
        If UBound(strReplaceStringArray) <> 1 Then
            '置換前後の文字列が正しく設定されていない場合は処理しない
            strErrMessage = "置換前後の文字列が正しく設定されていません。" & vbCrLf & "処理を終了します。"
        Else
            strBefore = strReplaceStringArray(0)
            strAfter = strReplaceStringArray(1)
            initialize = True
        End If
    End Function

    '前処理
    Public Function execPreProc()
        execPreProc = True
    End Function

    '処理
    Public Function exec(ByRef objGrepResult)
        With objGrepResult
            If strPreviousPath <> .strFilePath Then
                If strPreviousPath <> "" Then
                    Call Editor.FileSave()
                    Call Editor.FileClose()
                End If
                Call Editor.FileOpen(.strFilePath)
            End If
            If replaceLine(.lngRow, strBefore, strAfter) Then
                lngSize = lngSize + 1
            End If
            strPreviousPath = .strFilePath
        End With
        exec = True
    End Function

    '後処理
    Public Sub execPostProc()
        Call Editor.FileSave()
        Call Editor.FileClose()
    End Sub

    'マクロ名
    Public Function getTitle()
        getTitle = "GrepAndReplace"
    End Function

    '処理開始時メッセージ
    Public Function getNoticeMessage()
        getNoticeMessage = _
            "Grep結果に挙がっている全てのファイル&行に対し置換を行います。" & vbCrLf & _ 
            "Grep結果が自動的に閉じられますので、Grep結果を残したい場合は事前に保存をしてください。" & vbCrLf & _
            "また実行時にはGrep結果以外のファイルは全て閉じてください。" & vbCrLf & _
            vbCrLf & _
            "置換前文字列:""" & strBefore & """" & vbCrLf & _
            "置換後文字列:""" & strAfter & """" 
    End Function

    '処理終了時メッセージ
    Public Function getEndMessage()
        getEndMessage = lngSize & "件置換しました。"
    End Function

    'エラー時メッセージ
    Public Function getErrMessage()
        getErrMessage = strErrMessage
    End Function

    Public Sub setErrMessage(ByRef strArg)
        strErrMessage = strArg
    End Sub

    '指定行のみを置換
    Private Function replaceLine(ByRef lngRow, ByRef strBfr, ByRef strAft)
        replaceLine = False
        Dim lngR
        Dim strOrgLine
        With Editor
            Call .Jump(lngRow, 1)
            strOrgLine = .GetLineStr(lngRow)
            Call .GoLineTop(1)
            Call .BeginSelect()
            Call .Jump(lngRow + 1, 1)
            If .GetLineCount(0) <> lngRow Then
                Call .GoLineTop(1)
                Call .Left_Sel()
            End If
            Call .ReplaceAll(strBfr, strAft, lngOption)
            If strOrgLine = .GetLineStr(lngRow) Then
                replaceLine = False
            Else
                replaceLine = True
            End If
        End With
    End Function

End Class

'ここから下は流用可能なロジック
'---------------------------------------------------------------------
Call manageMain()

'メイン処理の実行
Sub manageMain()
    'Strategyのインスタンス生成
    Dim objStrategy: Set objStrategy = New Strategy
    '初期化処理
    If objStrategy.initialize() = False Then
        '初期化処理に失敗した場合は処理終了
        MsgBox objStrategy.getErrMessage()
        Set objStrategy = Nothing
        Exit Sub
    End If

    '処理開始可否確認
    Dim objDialog: Set objDialog = CreateObject("WScript.Shell")
    Dim lngRtn: lngRtn = objDialog.Popup(objStrategy.getNoticeMessage(), 0, objStrategy.getTitle(), 1)
    If lngRtn = 1 Then
        If execMain(objStrategy) Then
            '正常に処理した場合
            MsgBox objStrategy.getEndMessage()
        Else
            '処理中断した場合
            MsgBox objStrategy.getErrMessage()
        End If
    Else
        MsgBox "処理を中止しました。"
    End If
    Set objDialog = Nothing
    Set objStrategy = Nothing
End Sub

'メイン処理
Function execMain(Byref objStrategy)
    execMain = False
    'Grepされたフォルダのパスを取得
    Dim strFolderPath: strFolderPath = getFolderPath()
    If strFolderPath = "" Then
        Call objStrategy.setErrMessage("Grep結果ファイルが想定外のフォーマットです。")
        Exit Function
    End If
    'Grep結果の取得
    Dim objGrepResults: objGrepResults = getGrepResults(strFolderPath)
    'Grep結果のファイルを閉じる
    Editor.FileClose()
    '前処理
    If objStrategy.execPreProc() Then
        '前処理が正常な場合のみ実行
        Dim blnErr: blnErr = False
        Dim lngIu: lngIu = UBound(objGrepResults)
        Dim lngI
        For lngI = 0 To lngIu
            '本処理
            If objStrategy.exec(objGrepResults(lngI)) = False Then
                '処理が正常終了しなかった場合は終わり
                blnErr = True
                Exit For
            End If
        Next
        If blnErr = False Then
            execMain = True
        End If
    End If
    '後処理
    Call objStrategy.execPostProc()
End Function

'対象ファイルのパスと行を全て取得する。
Function getGrepResults(Byref strFolderPath)
    Dim lngRu: lngRu = Editor.GetLineCount(0)
    Dim lngR
    Dim lngI: lngI = -1
    Dim objGrepResults(): Redim objGrepResults(lngRu)
    Dim strLine
    For lngR = lngRu To 1 Step -1
        strLine = getLine(lngR)
        If beginWith(strLine, strFolderPath) Then
            lngI = lngI + 1
            Set objGrepResults(lngI) = getGrepResult(strLine)
        End If
    Next
    Redim Preserve objGrepResults(lngI)
    getGrepResults = objGrepResults
End Function

'対象ファイルのパスと行を取得する。
Function getGrepResult(ByRef strArgLine)
    Dim objGrepResult: Set objGrepResult = New GrepResult
    With objGrepResult
        .strLine = strArgLine
        .strFilePath = getFilePath(strArgLine)
        .lngRow = getRow(strArgLine)
        .lngCol = getCol(strArgLine)
    End With
    Set getGrepResult = objGrepResult
    Set objGrepResult = Nothing
End Function

'Grep結果から行を取得する。
Function getLine(ByRef lngR)
    getLine = getString(Editor.GetLineStr(lngR))
End Function

'末尾の改行を削除した文字列を取得する。
Function getString(ByRef strLine)
    If endWith(strLine, vbCrLf) Then
        getString = Left(strLine, Len(strLine) - Len(vbCrLf))
    ElseIf endWith(strLine, vbCr) Then
        getString = Left(strLine, Len(strLine) - Len(vbCr))
    ElseIf endWith(strLine, vbLf) Then
        getString = Left(strLine, Len(strLine) - Len(vbLf))
    Else
        getString = strLine
    End If
End Function

'Grep結果から対象フォルダを取得する。
Function getFolderPath()
    Const STR_FOLDER_PREFIX = "フォルダ   "
    getFolderPath = ""
    Dim lngRu: lngRu = Editor.GetLineCount(0)
    Dim lngR
    Dim strLine
    For lngR = 1 To lngRu
        strLine = getLine(lngR)
        If beginWith(strLine, STR_FOLDER_PREFIX) Then
            getFolderPath = Mid(strLine, Len(STR_FOLDER_PREFIX) + 1)
            Exit Function
        End If
    Next
End Function

'strArg0がstrArg1で始まる文字列か。
Function beginWith(ByRef strArg0, ByRef strArg1)
    beginWith = False
    If Len(strArg0) < Len(strArg1) Then
        Exit Function
    End If
    If InStr(1, strArg0, strArg1) = 1 Then
        beginWith = True
    End If
End Function

'strArg0がstrArg1で終わる文字列か。
Function endWith(ByRef strArg0, ByRef strArg1)
    endWith = False
    If Len(strArg0) < Len(strArg1) Then
        Exit Function
    End If
   
    If Right(strArg0, Len(strArg1)) = strArg1 Then
        endWith = True
    End If
End Function


'Grep結果からファイルのパスを取得する。
Function getFilePath(ByRef strArgLine)
    getFilePath = ""
    Dim lngPlPos: lngPlPos = getPlPos(strArgLine)
    Dim strPath: strPath = Left(strArgLine, lngPlPos - 1)
    getFilePath = strPath
End Function

'Grep結果から行を取得する。
Function getRow(ByRef strArgLine)
    getRow = 0
    Dim lngPlPos: lngPlPos = getPlPos(strArgLine)
    Dim lngCmmPos: lngCmmPos = getCmmPos(strArgLine)
    Dim strRow: strRow = Mid(strArgLine, lngPlPos + 1, lngCmmPos - lngPlPos - 1)
    getRow = CLng(Trim(strRow))
End Function

'Grep結果から列を取得する。
Function getCol(ByRef strArgLine)
    getCol = 0
    Dim lngCmmPos: lngCmmPos = getCmmPos(strArgLine)
    Dim lngPrPos: lngPrPos = getPrPos(strArgLine)
    Dim strCol: strCol = Mid(strArgLine, lngCmmPos + 1, lngPrPos - lngCmmPos - 1)
    getCol = CLng(Trim(strCol))
End Function

'"("の位置を取得
Function getPlPos(ByRef strArgLine)
    getPlPos = -1
    Dim lngCrnPos: lngCrnPos = InStr(3, strArgLine, ":")
    Dim lngP
    Dim lngPlPos
    For lngP = lngCrnPos To 3 Step -1
        If Mid(strArgLine, lngP, 1) = "(" Then
            lngPlPos = lngP
            Exit For
        End If
    Next
    getPlPos = lngPlPos
End Function

'","の位置を取得
Function getCmmPos(ByRef strArgLine)
    getCmmPos = -1
    Dim lngPlPos: lngPlPos = getPlPos(strArgLine)
    Dim lngCmmPos: lngCmmPos = InStr(lngPlPos, strArgLine, ",")
    getCmmPos = lngCmmPos
End Function

'")"の位置を取得
Function getPrPos(ByRef strArgLine)
    getPrPos = -1
    Dim lngCmmPos: lngCmmPos = getCmmPos(strArgLine)
    Dim lngPrPos: lngPrPos = InStr(lngCmmPos, strArgLine, ")")
    getPrPos = lngPrPos
End Function

Class GrepResult
    Public strLine
    Public strFilePath
    Public lngRow
    Public lngCol
End Class

0 件のコメント:

コメントを投稿