RegExReplace: Can I modify the value of a backreference?

Post your working scripts, libraries and tools.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 10:23

Hi all,

Question:
Is it possible to modify the value of a backreference in RegExReplace before feeding it back as part of the replacement text?

I can of course do it with a workaround using RegExMatch, working with its RegExMatchInfo object to manipulate the whole text each time a match is found, and searching multiple times with increasing starting positions until no more matches are found. But, as I wrote, I consider this more of a workaround.

I also looked into the RegEx Callouts option, but any solution with that seems to be even more workaroundish than with RegExMatch, unless I did not fully understand its capabilities?

I just thought, maybe there is a quick and easy way with RegExReplace, that I either overlooked or that is not documented officially?
Does anyone of you readers know of such a way?

MTIA and regards, a.
Last edited by BoBo on 24 Jan 2023, 11:17, edited 1 time in total.
Reason: Topic moved from 'Ask For Help' to 'AutoHotkey v2 Scripts and Functions' on request by the OP.
User avatar
mikeyww
Posts: 27267
Joined: 09 Sep 2014, 18:38

Re: RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 10:29

I believe that the answer is "no". Although you can use the reference in a larger string, you cannot modify the reference itself.
Last edited by mikeyww on 23 Jan 2023, 10:32, edited 1 time in total.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 10:31

Ok, I was afraid of that but thanks for the confirmation.
User avatar
mikeyww
Posts: 27267
Joined: 09 Sep 2014, 18:38

Re: RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 10:35

I believe that you also cannot wrap the backref in a function.

Code: Select all

#Requires AutoHotkey v2.0
str   := "abcd"
regex := ".+(c).*"
MsgBox RegExReplace(str, regex, "$1")
MsgBox RegExReplace(str, regex, SubStr("$1", 1, 1))
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 10:43

Yes, thank you for brainstorming.
This was the first idea that came to my mind as well, but I never tried it because it would not make any sense in connection with how RegExReplace is described in the help. So, I agreeingly also assume that it cannot be done, just as you wrote.
User avatar
mikeyww
Posts: 27267
Joined: 09 Sep 2014, 18:38

Re: RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 10:46

When you have the result, you can use it in any way, of course.

Code: Select all

#Requires AutoHotkey v2.0
str   := "abcd"
regex := ".+(c).*"
MsgBox RegExReplace(str, regex, "$1")               ; OK
MsgBox RegExReplace(str, regex, SubStr("$1", 1, 1)) ; No
MsgBox SubStr(RegExReplace(str, regex, "$1"), 1, 1) ; OK
I think of it as a specific placeholder that is only used in that parameter of RegExReplace. It cannot be used as an expression.

Code: Select all

#Requires AutoHotkey v2.0
str   := "ab1d"
regex := ".+(1).*"
MsgBox RegExReplace(str, regex, "$1")
MsgBox RegExReplace(str, regex, 3 + "$1")
From the documentation:
Replacement: The string to be substituted for each match, which is plain text (not a regular expression).
viewtopic.php?f=86&t=113023
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: RegExReplace: Can I modify the value of a backreference?

23 Jan 2023, 15:09

Ok, I now threw toghether some lines of code to try to make my own _RegExReplace_Enhanced function that would allow what I want.
Whoever has the time and nerves needed, please have a look and give advice as to how I can improve it, so that it would be more professional. Many many thanks in advance (MMTIA)!

How it works:
In general, it works like RegExReplace but instead of the Replacement parameter it expects the name of a user written function (in the form of a string). That function must receive a RegExMatchInfo object as parameter and can, thus, manipulate also all values of backreferences to matched subpatterns. The function must return a string that is used as replacement for each found match.

An application example:

Code: Select all

vOrgString := 'abc="95.123"def="12.456789"ghi'
vNewString := _RegExReplace_Enhanced(vOrgString, '="(\d+\.\d+)"', "_FixNumberInMatch", &vCount)
MsgBox vNewString "`n`nCount of matches: " vCount

_FixNumberInMatch(vMatchInfo_ob) {
    return '="' Ceil(vMatchInfo_ob[1]) '"'
}
The message box should display
abc="96"def="13"ghi

Count of matches: 2
Here is the actual _RegExReplace_Enhanced function:

Code: Select all

_RegExReplace_Enhanced(pHaystack, pNeedleRegEx, pReplacementFunction?, &pOutputVarCount := 0, pLimit := -1, pStartingPos := 1) {
    if IsSet(pReplacementFunction) and not HasMethod(%pReplacementFunction%, , 1) {
        ; return ""
        return pHaystack
    }
        
    vFoundMatches_Count := 0
    vStartingPos := pStartingPos
    if vStartingPos <= 0 {
        vStartingPos := StrLen(pHaystack) + 1 - (-1 * vStartingPos) ; compare RegExMatch
    }
    vWorkingString := SubStr(pHaystack, 1, vStartingPos - 1)

    while (pLimit = -1 or vFoundMatches_Count < pLimit) and (vFoundPos := RegExMatch(pHaystack, pNeedleRegEx, &vMatchInfo_ob, vStartingPos)) > 0 {
        vFoundMatches_Count++
        vSubStringFromLastStartingPosToFoundMatch := SubStr(pHaystack, vStartingPos, vFoundPos - vStartingPos)
        vStartingPos := vFoundPos + StrLen(vMatchInfo_ob[0])

        vWorkingString .= vSubStringFromLastStartingPosToFoundMatch
        
        ; vReplacementString := ""
        vReplacementString := vMatchInfo_ob[0]
        if IsSet(pReplacementFunction) {
            try vReplacementString := %pReplacementFunction%(vMatchInfo_ob)
            catch {
                ;
            }
        }
        vWorkingString .= vReplacementString
    }

    pOutputVarCount := vFoundMatches_Count
    return vWorkingString . SubStr(pHaystack, vStartingPos)
}
Last edited by autocart on 25 Jan 2023, 08:21, edited 1 time in total.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: RegExReplace: Can I modify the value of a backreference?  Topic is solved

24 Jan 2023, 10:58

UPDATE:
I just improved the "error handling" a little bit.

Code: Select all

_RegExReplace_Enhanced(pHaystack, pNeedleRegEx, pReplacementFunction?, &pOutputVarCount := 0, pLimit := -1, pStartingPos := 1) {
    if IsSet(pReplacementFunction) and not HasMethod(%pReplacementFunction%, , 1) {
        MsgBox "Problem 1: The passed replacement function can not be executed. Haystack is not changed."
        ; return ""
        return pHaystack
    }
        
    vFoundMatches_Count := 0
    vStartingPos := pStartingPos
    if vStartingPos <= 0 {
        vStartingPos := StrLen(pHaystack) + 1 - (-1 * vStartingPos) ; compare RegExMatch
    }
    vWorkingString := SubStr(pHaystack, 1, vStartingPos - 1)

    while (pLimit = -1 or vFoundMatches_Count < pLimit) and (vFoundPos := RegExMatch(pHaystack, pNeedleRegEx, &vMatchInfo_ob, vStartingPos)) > 0 {
        vFoundMatches_Count++
        vSubStringFromLastStartingPosToFoundMatch := SubStr(pHaystack, vStartingPos, vFoundPos - vStartingPos)
        vStartingPos := vFoundPos + StrLen(vMatchInfo_ob[0])

        vWorkingString .= vSubStringFromLastStartingPosToFoundMatch
        
        ; vReplacementString := ""
        vReplacementString := vMatchInfo_ob[0] ; just trying to be extra safe
        if IsSet(pReplacementFunction) {
            try vReplacementString := %pReplacementFunction%(vMatchInfo_ob)
            catch {
                MsgBox "Problem 2: There was a problem while trying to execute the replacement function. Haystack is not changed."
                return pHaystack
            }
        } else {
            MsgBox "Problem 3: The passed parameter for the replacement function seems to not be set. Haystack is not changed."
            return pHaystack
        }
        vWorkingString .= vReplacementString
    }

    pOutputVarCount := vFoundMatches_Count
    return vWorkingString . SubStr(pHaystack, vStartingPos)
}
Last edited by autocart on 25 Jan 2023, 08:21, edited 1 time in total.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: RegExReplace: Can I modify the value of a backreference?

25 Jan 2023, 06:07

Just as a side note for the record:

The function also works well for generally executing some sub-function based on each found match, e.g. for data extraction (without having to necessarily change haystack).

Probably, there could be faster code, but for me this seems quite a comfortable solution so far.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: RegExReplace: Can I modify the value of a backreference?

25 Jan 2023, 08:25

UPDATE:
I had a bug in the code in line 10, respectively, 11. Fixed it in both versions up above.
New, correct line:
vStartingPos := StrLen(pHaystack) + 1 - (-1 * vStartingPos) ; compare RegExMatch
Old, wrong line:
vWorkingString := StrLen(pHaystack) + 1 - (-1 * vStartingPos) ; compare RegExMatch

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: No registered users and 16 guests