Modify

#588 closed Feature Request (Rejected)

StringRegExpReplaceCallback

Reported by: RobSaunders Owned by: Valik
Milestone: Component: AutoIt
Version: Severity: None
Keywords: string regular expression replace callback Cc: admin@…

Description

I know that it's annoying when people compare AutoIt to other languages and say "Hey they have this function, you should too!" but at the risk of being hypocritical here, I really think this function would be handy.
PHP has a function called preg_replace_callback (preg_replace is basically our StringRegExpReplace). Instead of passing a static string as the 'replace' parameter, you provide a callback function. At the very simplest of possibilities, this could allow for code like this:

$text = "Hello, my name is Rob and I'm here today to tell you all about sponges! These wonderful things have made a huge impact on life in modern times"
$text = StringRegExpReplaceCallback($text, '(t[^ ]+)', '_Capital')

Func _Capital($matches)
	Return StringUpper($matches[0])
EndFunc

The callback function is passed an array with each group as an element (so if my pattern had been something like '(t[^ ]+) ([^ ]+)' then $matches would have two elements), and the function returns what the replacement string should be. In the case of my example, it simply capitalizes every word that starts with T.
I wouldn't be asking for this if I thought it was possible with a UDF, but after looking at it for a while I simply can't figure out a fool proof way to do it, so that's why I made this ticket.

Thanks for reading.

Attachments (0)

Change History (12)

comment:1 by TicketCleanup, on Sep 21, 2008 at 4:00:05 AM

Version: Other

Automatic ticket cleanup.

comment:2 by Valik, on Sep 26, 2008 at 3:53:14 AM

Owner: set to Valik
Status: newassigned

I'm not even going to pretend I understand why it's possible to implement this feature, but some studying of how the current StringRegExpReplace() is implemented leads me to believe it should be possible to add a version that takes a callback and that it will behave like you expect.

I'll see what I can do for a future release.

comment:3 by MsCreatoR <mscreator@…>, on Oct 1, 2008 at 11:41:20 AM

I am not sure that i fully understand the concept of the idea, but here is my attempt to build such functionality:

$sString = "This is a Test, a _StringRegExpReplaceCallBack test"
$sRetVar = _StringRegExpReplaceCallBack($sString, "(?i)\bt[^ ]+", "_Capital")

MsgBox(64, "Results", StringFormat("Initial String:\n%s\n\nReturn String:\n%s", $sString, $sRetVar))

Func _StringRegExpReplaceCallBack($sTest, $sPattern, $sFunction)
	Local $aMatches = StringRegExp($sTest, $sPattern, 3)
	Local $sReplace
	
	For $i = 0 To UBound($aMatches)-1
		$sReplace = Call($sFunction, $aMatches[$i])
		If @error Then Return SetError(1, 0, $sTest)
		
		$sTest = StringReplace($sTest, $aMatches[$i], $sReplace, 1, 1)
	Next
	
	Return $sTest
EndFunc

Func _Capital($sMatch)
	Return StringUpper($sMatch)
EndFunc

Or am i completely misunderstood the concept?

comment:4 by Valik, on Oct 1, 2008 at 6:20:49 PM

It's possible that StringReplace() will replace something that does not match the regular expression but does match the captured portion of a regular expression.

This really can't be done via UDF. While it's true that you can do what you want to the array of matches it's not really possible to reliably re-build the string because the non-replaced portions are known only to the RE engine internally. All you can get from AutoIt is the full string, a list of matches and the replacements but you can't actually match them up perfectly.

comment:5 by Valik, on Oct 1, 2008 at 6:42:13 PM

Here's an example using your code why this does not work:

$sString = "ababab"
$sRetVar = _StringRegExpReplaceCallBack($sString, ".*(ab)", "_Replace")

MsgBox(64, "Results", StringFormat("Initial String:\n%s\n\nReturn String:\n%s", $sString, $sRetVar))

Func _StringRegExpReplaceCallBack($sTest, $sPattern, $sFunction)
	Local $aMatches = StringRegExp($sTest, $sPattern, 3)
	Local $sReplace

	For $i = 0 To UBound($aMatches)-1
		$sReplace = Call($sFunction, $aMatches[$i])
		If @error Then Return SetError(1, 0, $sTest)

		$sTest = StringReplace($sTest, $aMatches[$i], $sReplace, 1, 1)
	Next

	Return $sTest
EndFunc

Func _Replace($sMatch)
	Return "cd"
EndFunc

It outputs "cdabab" but the correct output would be "ababcd". The greedy .* at the beginning eats the leading "abab" and thus the only capture is the trailing "ab". Your code, due to the simple nature of StringReplace(), only replaces the first "ab" which is wrong.

comment:6 by amel27, on Oct 8, 2008 at 7:08:51 AM

$sString  = "ababab"
$sRetVar  = _StringRegExpReplaceEx($sString, ".*(ab)")

MsgBox(64, "Results", StringFormat("Initial String:\n%s\n\nReturn String:\n%s", $sString, $sRetVar))

Func _StringRegExpReplaceEx($sString, $sPattern, $sFunction = "_StringRegExpReplaceCallBack")
	Local $sSqueeze = StringRegExpReplace($sString, $sPattern, Chr(0))
	Local $aSqueeze = StringSplit($sSqueeze, Chr(0))
	Local $sResult = ""

    Local $aM1 = StringRegExp($sString, $sPattern, 4), $aM2
    If IsArray($aM1) Then
        For $i=0 To UBound($aM1)-1
            Local $as[10]=['','','','','','','','','','']
            $aM2=$aM1[$i]
            For $j=0 To UBound($aM2)-1
                $as[$j]=$aM2[$j]
            Next
            $sResult &= $aSqueeze[$i+1] & Call($sFunction, $as)
        Next
    EndIf
    Return $sResult & $aSqueeze[$aSqueeze[0]]
EndFunc

Func _StringRegExpReplaceCallBack($aGroups)
	ConsoleWrite("Replaced string: "& $aGroups[0] &@CRLF)
	ConsoleWrite("Group #1 string: "& $aGroups[1] &@CRLF)

	Return "cd"
EndFunc

comment:7 by Valik, on Oct 8, 2008 at 4:52:43 PM

It's funny, most of the time people ask for things that are just better to be implemented as UDF's. Somebody actually asks for something that would be better to be written as a built-in function and suddenly there's a rush to implement it as a UDF.

in reply to:  7 comment:9 by anonymous, on Oct 10, 2008 at 4:03:54 AM

Replying to Valik:

It's funny, most of the time people ask for things that are just better to be implemented as UDF's. Somebody actually asks for something that would be better to be written as a built-in function and suddenly there's a rush to implement it as a UDF.

Thank you for verifying my assumption that it would indeed be better implemented as a built in function.

comment:10 by anonymous, on Sep 7, 2010 at 3:53:10 AM

How about this method?

$asdf = 'aaa bbb cc"c ddd eee'
$as = StringRegExpReplaceCallback($asdf , "aaa|bbb|ccc" , "upper")
MsgBox(0 ,"Callback?", $as)

Func StringRegExpReplaceCallback($string , $pattern , $callback)

Return StringReplace(Execute('"' & StringRegExpReplace(StringReplace($string , '"' , "\x22") , $pattern , StringFormat('" & %s("\0") & "' , $callback)) & '"') , "\x22" , '"')

EndFunc

Func upper($text)

Return StringUpper($text)

EndFunc

comment:11 by Matt Diesel, on Oct 4, 2010 at 8:24:34 AM

Although thats a clever method, it's got flaws so it's only good for your use when you know what not to do. My method here is a lot more complete.

comment:12 by Jon, on Jul 21, 2013 at 11:32:31 PM

Resolution: Rejected
Status: assignedclosed

Modify Ticket

Action
as closed The owner will remain Valik.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.