RegEx issue. Of course.... Topic is solved

Get help with using AutoHotkey and its commands and hotkeys
User avatar
theimmersion
Posts: 181
Joined: 09 Jul 2016, 08:34
Location: Serbia

RegEx issue. Of course....

08 Feb 2018, 19:09

I tried a bunch of googling and jeesweg's examples and just could not solve the GA RegEx. -.-
Im trying to extract any and all matches with RegExMatch for some anims. I want to modify the anims easy and create sequences so the easiest way i thought was to use variables with multiple entries. I did not actually start on it, trying to find the solution to separate stuff first.
So, anyone know the regex thing for extracting something like this:

I tried mutliple RegEx combos, this being the latest.

Code: Select all

; The anim instructins that should take place.
AnimSeq=[Action: PlayOnce] [Move: X "500"] [Move: Y "200"] [Action: AnimEnd]
AnimSeq2=[Action: Play "3"] [Move: X "500"] [Move: Y "300"] [FrameCount: 35] [Action: Finish]
; Remove spaces because its one constants not needed for any evaluations?
StringReplace, AnimSeq, AnimSeq, %A_Space%, , All
; Find the d*** things and store them into separate Command[] array.
MatchCount:=RegExMatch(AnimSeq,"i)(\[Action:.+?])+|(\[Move:.+?])",Command)

; Loop through the RegEx results, later should be replaced with actual animation instructions.
Loop %MatchCount%
{
	ShowCommand:=Command%A_Index%
	tooltip  %A_Index%/%MatchCount%`n----------`n%ShowCommand%, 700, 200
	Sleep 1000
}
tooltip

Return
Now, i could use tons of loops that would match one by one and removing one entry at a time but i thought a regex match would be much more efficient because it already sets any match to an array that i can later parse. Any thoughts?
FanaticGuru
Posts: 1193
Joined: 30 Sep 2013, 22:25

Re: RegEx issue. Of course....

09 Feb 2018, 01:31

RegExMatch does not work the way you think it does.

It does not return MatchCount because it only ever matches once and it returns the position of that one match. Within that one match it might capture multiple subpatterns.

To find multiple matches of the needle pattern you have to use multiple RegExMatch, preferably in a loop of some sort.

Maybe this will help start the learning process.

Code: Select all

Haystack = 
(Join`n
The first date is January 5, 2017
The second date is February 12, 2018
)

Needle := "m`aU)date is (.+) (\d+), (\d+)$"

X:=1
while (X := RegExMatch(Haystack, Needle, M, X+StrLen(M)))
    MsgBox % "Month = " M1 "`nDay = " M2 "`nYear = " M3
The while code is optimized for compactness which makes it a little more cryptic but you don't really have to understand it completely to use it.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts

AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon

[Function] Timer - Create and Manage Timers
User avatar
theimmersion
Posts: 181
Joined: 09 Jul 2016, 08:34
Location: Serbia

Re: RegEx issue. Of course....

09 Feb 2018, 04:02

Thanks for trying to explain but that didnt help me much. xD
I guessed i need a loop but perhaps i didnt give a proper example since i never got it to work so i did not have any loops.
My idea was (to summarize in theory):

Code: Select all


Sequence1=Action PlayOnce
Sequence2=Move X 500
Sequence3=Move Y 300
Sequence3=Action Finish
Loop
{
	Sequence:=Sequence%A_Index%
	If InStr(Sequence, "Action Finish")
	{
		Break
	}
	If InStr(Sequence, "Action Play")
	{
		
	}
	If InStr(Sequence, "Move X")
	{
		StringReplace, Sequence, Sequence, Move X,
		WinMove do stuff
	}
	If InStr(Sequence, "Move Y")
	{
		StringReplace, Sequence, Sequence, Move Y,
		WinMove do stuff
	}
	
}
So i want to turn this:

Code: Select all

Sequence1=Action PlayOnce
Sequence2=Move X 500
Sequence3=Move Y 300
Sequence3=Action Finish
into this:

Code: Select all

; Set anim stuff like move and other stuff it should do.
AnimSeq=[Move X 500] [Move Y 300] [Action Pause 3000] [Move X 500] [Move Y 300] [Action Pause 3000] [Action StartAnim AnimSeq2]
AnimSeq2=[Move X 500] [Move Y 300] [Action Finish]

GetAnimProps:
; Or if it requires a loop, i still need a count of all the different commands in AnimSeq Var and need to extract them in sequence
MatchCount:=RegExMatch(AnimSeq,"i)(\[Action:.+?])+|(\[Move:.+?])",Command)
Loop %MatchCount%
	Sequence%A_Index%:=Command%A_Index%
; OR
MatchCount:=Somehow get the count? or wait for end or repetition/empty var and break the loop?
Loop %MatchCount%
	Sequence%A_Index%:=RegExMatch(AnimSeq,"i)(\[Action:.+?])+|(\[Move:.+?])",Command) ; where should i put A_Index to move onwards and what needle code i need?

Loop
{
	Sequence:=Sequence%A_Index%
	If InStr(Sequence, "Action Finish")
		Break
	else If InStr(Sequence, "Action Pause")
	{
		StringReplace, Sequence, Sequence, Action Pause, 
		Sleep %Sequence%
	}
	else If InStr(Sequence, "Action StartAnim")
	{
		StringReplace, Sequence, Sequence, Action StartAnim, 
		AnimSeq:=Sequence
		GoSub GetAnimProps
		Break
	}
	else If InStr(Sequence, "Move X")
	{
		StringReplace, Sequence, Sequence, Move X,
		WinMove do stuff
	}
	else If InStr(Sequence, "Move Y")
	{
		StringReplace, Sequence, Sequence, Move Y,
		WinMove do stuff
	}
}
I can track down a single occurance but there can be multiple of same type but with different instructions so i need to extract them in sequence.

At least thats how it looks in my head. Im trying to condense code and make it easier to overview and edit an animation.
Look at AnimSeq as an animation event that will do a particular thing and if you command a new AnimSeq, a new anim event takes place until finish is encountered.
Or to go the manual way and add every sequence manually in order with instructions that will eat a lot of lines.
I know about while but i need loop so can perhaps add more functionality. In while, its limited. Also, its easier to track. :)
Sorry for the wall post, trying to be as clear as possible and im still not good enough in english to do it in fewer lines i guess. -.-
FanaticGuru
Posts: 1193
Joined: 30 Sep 2013, 22:25

Re: RegEx issue. Of course....  Topic is solved

09 Feb 2018, 15:33

Here is a more specific example of what you seem to want.

Code: Select all

AnimSeq=[Move X 500] [Move Y 300] [Action Pause 3000] [Move X 500] [Move Y 300] [Action Pause 3000] [Action StartAnim AnimSeq2]

Needle := "U)\[(Action|Move) (.*)\]"

Sequence := {}
X:=1
while (X := RegExMatch(AnimSeq, Needle, Command, X+StrLen(Command)))
    Sequence.Push({"Command":Command1, "Info":Command2})
    
; Display Results
MsgBox % "Number of Elements in Sequence = " Sequence.MaxIndex()
for Index, Element in Sequence
    MsgBox % "Index = " Index "`nCommand = " Element.Command "`nInfo = " Element.Info
If you don't need the commands broken up into the "Command" (ie Move) and the "Info" (ie X 500) then you can do it simpler without Regex.

Code: Select all

AnimSeq=[Move X 500] [Move Y 300] [Action Pause 3000] [Move X 500] [Move Y 300] [Action Pause 3000] [Action StartAnim AnimSeq2]

Sequence := StrSplit(AnimSeq, "]", "[ ")
Sequence.Pop()
; Display Results
for Index, Command in Sequence
    MsgBox % "Index = " Index "`nCommand = " Command
If you are not pulling the AnimSeq data from somewhere already in the format given and instead are actually entering this information then a format change makes it be put into an array directly.

Code: Select all

AnimSeq :=["Move X 500","Move Y 300","Action Pause 3000","Move X 500","Move Y 300","Action Pause 3000","Action StartAnim AnimSeq2"]
; Display Results
for Index, Command in AnimSeq
    MsgBox % "Index = " Index "`nCommand = " Command
FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts

AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon

[Function] Timer - Create and Manage Timers
User avatar
theimmersion
Posts: 181
Joined: 09 Jul 2016, 08:34
Location: Serbia

Re: RegEx issue. Of course....

09 Feb 2018, 15:42

Nice! 3 examples. Ill play around later and post which one works best for me. Maybe use all for different things. In any case, all 3 are awesome examples. Thanks!
User avatar
Masonjar13
Posts: 1396
Joined: 20 Jul 2014, 10:16
GitHub: Masonjar13
Location: Не Россия

Re: RegEx issue. Of course....

09 Feb 2018, 15:50

Quick (possibly) helpful note: RegExReplace can be used here.

Code: Select all

RegExReplace(str,regExpression,,cnt)
msgbox % cnt
FanaticGuru
Posts: 1193
Joined: 30 Sep 2013, 22:25

Re: RegEx issue. Of course....

09 Feb 2018, 16:09

Masonjar13 wrote:Quick (possibly) helpful note: RegExReplace can be used here.

Code: Select all

RegExReplace(str,regExpression,,cnt)
msgbox % cnt
It is a good point that RegExReplace can be used to "count" the occurances of a pattern in a string. But as not to side track those with limited knowledge RegExReplace cannot capture patterns.

Which seems to be the confusion with the OP, in that RegExMatch will not find every occurance of a pattern the same way that RegExReplace does.

RegExMatch finds one occurance of a pattern and can return the pattern found. You have to do a separate RegExMatch for every occurance of a pattern in a string.

RegExReplace can find all occurances of a pattern and can change the pattern but does not return information from the pattern.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts

AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon

[Function] Timer - Create and Manage Timers
User avatar
theimmersion
Posts: 181
Joined: 09 Jul 2016, 08:34
Location: Serbia

Re: RegEx issue. Of course....

09 Feb 2018, 16:32

Well, could RegExReplace use AnimSeq in a loop and set a loop index variable with first match and remove that match from AnimSeq?
Like
Loop
{
FoundString := command%a_index% := regexre(AnimSeq, exp)
StringReplace, AnimSeq, FoundString,, 0
; here would be FoundString reformated to use the end value
}
Thats how I guess RegExReplace could be used?
Now really interested how far I missed. :D
FanaticGuru
Posts: 1193
Joined: 30 Sep 2013, 22:25

Re: RegEx issue. Of course....

09 Feb 2018, 17:18

theimmersion wrote:Well, could RegExReplace use AnimSeq in a loop and set a loop index variable with first match and remove that match from AnimSeq?
Like
Loop
{
FoundString := command%a_index% := regexre(AnimSeq, exp)
StringReplace, AnimSeq, FoundString,, 0
; here would be FoundString reformated to use the end value
}
Thats how I guess RegExReplace could be used?
Now really interested how far I missed. :D
Remember where I said, "But as not to side track those with limited knowledge RegExReplace cannot capture patterns."

This is not the droid you are looking for.

There are Help documents that contain lots of information.
StringReplace < click for documents.

No where in there is anything about "FoundString". Every parameter position designated by commas after a command has a very specific and preset meaning and usage. Wanting them and using them as something different will not work.

Forget all about RegExReplace as nothing in your post appears to have to do with replacing anything in a string (put stuff into a string). You are attempting to match things in a string (get stuff out of a string).

To get the code you want I would advise studying the three examples give as they demonstrate a lot of very useful techniques about RegExMatch, objects, for-loop, etc. RegEx is a powerful sub-discipline but objects are what takes most people to the next level. Sequences of data, that is what arrays were made for which is one of the simplest objects. Probably a lot of what you want to accomplish with your overall script could be made easier by a solid understanding of objects. Objects are the core of most popular programming languages. Windows itself is very object oriented.

You are trying. That is the first step to succeeding.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts

AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon

[Function] Timer - Create and Manage Timers
User avatar
Masonjar13
Posts: 1396
Joined: 20 Jul 2014, 10:16
GitHub: Masonjar13
Location: Не Россия

Re: RegEx issue. Of course....

09 Feb 2018, 17:29

Was the objective to get all the matches? There is a bit of a hackish way to do it only calling RegExReplace and RegExMatch once.

Code: Select all

regExReplace(str,regExpression,,cnt)
loop % cnt
    fregExpression.="(" . regExpression . ")" ; append expression "cnt" number of times, each acting as a subpattern
regExMatch(str,"O)" . fregExpression,strOut)
All matches would then be in strOut, accessed with strOut.value(2), strOut[2], or strOut.2, where 2 is the second match. This should technically be faster, since it's only calling each RegEx function once. You could also not use O), I just think it would be easier.
User avatar
theimmersion
Posts: 181
Joined: 09 Jul 2016, 08:34
Location: Serbia

Re: RegEx issue. Of course....

09 Feb 2018, 17:52

Masonjar13 wrote:Was the objective to get all the matches?
Uhm, if you mean in per [xxx] as independent entries, yes? Finding all matches is a broad question for this one. xDDDDD
Sorry for confustion. This is hard explaining sometimes. :oops:

I was just thinking in theory of how if at all possible how RegExReplace would be used.
But the task here was to accomplish to read one main variable that can have many different commands in any order and make a script do stuff based on them.
That is why it was kinda important to find a consistent and reliable way of getting each command within a single variable and separate them for further reformat.

Like, i could use any combination in any order of:

Code: Select all

[Move X "some number"]
[Move Y "some number"]
[Rotate "some positive or negative number"]
[FrameCount "some number"]
[Action "some keyword action to be executed"]
etc...
inside a single variable like:
AnimationSequence:=
in any order based on my needs for some animation i want to make on screen and the script would act based on them in sequence (hence called a sequence).
Starting with the first [command] than [command] what ever they might be.

I can make a gdi+ image rotate then move, or move than do some action, like change the image to something else, add more options etc.
Look at the AnimationSequence as some form of animation matinee. I can have any number of sequences that hold any combo of move/rotate/change image combo instructions to execute.

Thank you both for those awesome informations. I think i can safely mark it solved.
Might i just point out this as also a very important info:
FanaticGuru wrote: To get the code you want I would advise studying the three examples give as they demonstrate a lot of very useful techniques about RegExMatch, objects, for-loop, etc. RegEx is a powerful sub-discipline but objects are what takes most people to the next level. Sequences of data, that is what arrays were made for which is one of the simplest objects. Probably a lot of what you want to accomplish with your overall script could be made easier by a solid understanding of objects. Objects are the core of most popular programming languages. Windows itself is very object oriented.
and kind of a pep talk. :salute: :superhappy:
Cheers guys! :D
User avatar
theimmersion
Posts: 181
Joined: 09 Jul 2016, 08:34
Location: Serbia

Re: RegEx issue. Of course....

10 Feb 2018, 08:47

It would be nice to get some feedback how to rename the topic to help find this easier and more pecise for what it solves. xD
Not a programmer so i have no clue how to call it. :oops:

I have made a test script demo to try out with comments and tried to change some stuff that would make it easier for me and hopefully for other newcomers as well.

Code: Select all

#SingleInstance, Force
; This is my attempt to make it more clear and more easier to change. Based on FanaticGuru's example codes: https://autohotkey.com/boards/viewtopic.php?f=5&t=43975

; We are using BeginDelimiter and EndDelimiter to make it easier for us to change it and because some characters need escaping.
; The expression might change to require same characters as part of the expression system so we also want to make sure to escape delimiter only.
; Set the beginning and ending delimiters to separate different instructions.
BeginDelimiter=[
EndDelimiter=]
; Remarks
; Only use one character for delimiters.
; Care should be taken when choosing delimeters with the following in mind:
; It should be choosen based on readability, its usage - meaning it should not be required anywhere within the commands keywords,
; easy input.
; As an example for easy input: for a complex set of commands, these delimiters "{}" would be cumbersome for me to use since it requires holding shift.

; characters needing escaping:
; \.*?+[{|()^$
; We use RegExMatch on BeginDelimiter to find a character that needs escaping.
RegExMatch(BeginDelimiter,"(\\)|(\.)|(\*)|(\?)|(\+)|(\[)|(\{)|(\()|(\))|(^)|(\$)",Delimiter)
; Check if a match was found by making sure Delimiter variable is not empty (it found a match), then precede the found delimiter with a backslash "\" to escape it with RegExReplace.
if not Delimiter=""
	BeginDelimiter:=RegExReplace(BeginDelimiter, "(\\)|(\.)|(\*)|(\?)|(\+)|(\[)|(\{)|(\()|(\))|(^)|(\$)", "\" . Delimiter)
; Do the same for EndDelimiter.
RegExMatch(EndDelimiter,"(\\)|(\.)|(\*)|(\?)|(\+)|(\[)|(\{)|(\()|(\))|(^)|(\$)",Delimiter)
if not Delimiter=""
	EndDelimiter:=RegExReplace(EndDelimiter, "(\\)|(\.)|(\*)|(\?)|(\+)|(\[)|(\{)|(\()|(\))|(^)|(\$)", "\" . Delimiter)

; Show the delimiter result.
Msgbox, Beginning delimiter: %BeginDelimiter%`nEnding delimiter: %EndDelimiter%`nEscaped character: \

; We set the keywords to use for commands:
CommandKeyWords=action startanim|action pause|move x|move y|rotate
; I chose to use them based on literal action it will do but it can be anything as long as it is divided by "|" character (without quotes of course).

; This is the first set of sequences for some animation:
AnimSeq=[rotate 50] [Move X 500] [Move Y 300] [Action Pause 2000] [Move X 500] [Move Y 300] [Action Pause 2000] [Action StartAnim AnimSeq2]
AnimSeq2=[Move Y 500] [Action Pause 2000] [Move X 500]

; Since we want the ability to use mutliple sequences, we setup a global anim sequence data container that we point to the sequence itself.
; First sequence will be always the same, the default.
AnimSeqData:=AnimSeq
; FanaticGuru's magic:

; We setup a GoTo point for when an action has been recognized, mainly a new anim sequence that has to be executed.
ReadSequence:

Needle = U i)%BeginDelimiter%(%CommandKeyWords%) (.*)%EndDelimiter%

Sequence := {}
X:=1
while (X := RegExMatch(AnimSeqData, Needle, Command, X+StrLen(Command)))
    Sequence.Push({"Command":Command1, "Info":Command2})
    
; Display Results
MsgBox % "Number of Elements in Sequence = " Sequence.MaxIndex()
for Index, Element in Sequence
{
    MsgBox % "Index = " Index "`nCommand = " Element.Command "`nValue = " Element.Info
	
	CheckLastCommand:=Element.Command
	CheckLastValue:=Element.Info
	; HERE YOU CAN DO WHAT EVER IT IS YOU WANT WHEN A COMMAND IS RECOGNIZED AND IN BEWTEEN THEM
	; This means, its time for Your imagination! :)
	
	; To demo a simple but very important thing when "Action Pause" command is recognized.
	; We use a simple sleep %Amount%
	if (CheckLastCommand="Action Pause")
		Sleep %CheckLastValue%
}

; Here is a set of some basic actions that should always exist.
; Like actions for finishing, exiting and waiting.
; Its setup so that if no action command is detected, it will make the script wait in case some other features are to be used with hotkeys or other events.
; So we have a check for new animation, script exit and the return point.

; We are checking the last entry for something that has to happen when anim ends. 
; Like, start new animation sequence or exit the script etc... The sky is the limit.
; Here we want to start new animation.
if (CheckLastCommand="Action StartAnim")
{
	; We recognized a command and act accordingly.
	AnimSeqData:=%CheckLastValue%
	msgbox New anim sequence to play: %CheckLastValue%`nNew sequence data: %AnimSeqData%
	; We GoTo rather than GoSub because we are not expecting anything to happen outside loop.
	; You can tailor it by Your needs of course and use GoSub and do stuff after the command is recognized.
	GoTo ReadSequence
}

; We recognized the ExitApp action and are now closing the script.
if (CheckLastCommand="Action ExitApp")
{
	msgbox, The animation has finished and the script will now close.
	ExitApp
}

; Since we setup up a GoTo, we use the outside loop to return.
msgbox, The animation has finished.
Return
; But if You would use GoSub instead, You would probably need to use something similar to this:
if (CheckLastCommand="Action Return")
{
	msgbox, The animation has finished and is waiting.
	Return
}
; Of course You would also need to add it in the command keywords. :)
; Ill leave that to You. Hope You find this useful! Good Luck!

; KEEP DOING STUFF HERE WHEN ANIMATION HAS FINISHED BUT NO RETURN COMMAND WAS SET

return
Sorry if i over-explained some stuff.
Cheers :thumbup:
Helgef
Posts: 3192
Joined: 17 Jul 2016, 01:02
Contact:

Re: RegEx issue. Of course....

10 Feb 2018, 14:22

Masonjar13 wrote:This should technically be faster, since it's only calling each RegEx function once
It might, or might not, using muliple calls with simpler patterns can be faster than one with a complex pattern. It is case depenent. There was a thread which showed that something like regexreplace(str,"a|b|c") was slower than three calls regexreplace(str, "a"), ..., regexreplace(str, "c"), if I recall correctly.

Cheers.
User avatar
jeeswg
Posts: 5143
Joined: 19 Dec 2016, 01:58
Location: UK

Re: RegEx issue. Of course....

25 Feb 2018, 15:28

- I've added a bit more to my 'GET ALL MATCHES' section here:
jeeswg's RegEx tutorial (RegExMatch, RegExReplace) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=28031
- For the original question, here's a RegExMatch with while loop example, but also a StrSplit example. I would probably use StrSplit for something like this. Cheers.

Code: Select all

q:: ;get multiple matches via a while loop
vText := "[label1: abc][label2: def][label1: ghi][label2: jkl]"
vOutput := "", vPos := 1
while vPos := RegExMatch(vText, "O)\[(label1|label2): ([^\]]*)", o, vPos)
	vOutput .= o.1 " - " o.2 "`r`n", vPos += StrLen(o.0)
MsgBox, % vOutput
return

w:: ;parse a list based on where square brackets start/end
vText := "[label1: abc][label2: def][label1: ghi][label2: jkl]"
oArray := StrSplit(SubStr(vText, 2, -1), "][")
vOutput := ""
for vKey, vValue in oArray
{
	oTemp := StrSplit(vValue, ": ")
	MsgBox, % oTemp.1 " - " oTemp.2
}
return

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], Heck-R, Kellyzkorner_NJ, Merddyn, Scr1pter, swagfag, tm6464 and 70 guests