InStr() with negative occurence

Propose new features and changes
hotkeyguy
Posts: 170
Joined: 11 Oct 2014, 12:22

InStr() with negative occurence

08 Jul 2017, 20:00

Hello,

InStr() Occurrence [v1.0.90+]:
If Occurrence is omitted, it defaults to the first match of the Needle in Haystack. Specify 2 for Occurrence to return the position of the second match, 3 for the third match, etc.
My suggestion: Support a negative number, -1 for the last match, -2 for the second last match and so on.


Many thanks and greetings
hotkeyguy
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: InStr() with negative occurence

08 Jul 2017, 20:29

It surprised me this, I don't know why it isn't like that, plus you have to specify the zero/negative offset from the end as the start position, instead of the positive offset from the start.

I'll probably add this to my strings tutorial:
jeeswg's strings tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=32985

Code: Select all

q:: ;find the nth-to-last occurrence
vIsV1 := !!SubStr(1,0)
Loop, 5
{
	vNum := A_Index
	;MsgBox, % InStr("a a a a a", "a", 0, 0, vNum) ;AHK v1
	;MsgBox, % InStr("a a a a a", "a", 0, -1, vNum) ;AHK v2
	MsgBox, % InStr("a a a a a", "a", 0, vIsV1-1, vNum) ;AHK v1/v2 two-way compatible
}
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: InStr() with negative occurence

12 Jul 2017, 05:07

Actually, that's what I've been missing for quite a few times too: a method to find the last occurence of string N before position X.
I believe that's what the negative offset should've done from the very beginning.

For example, in a large pseudo-INI file one might find much faster the key name that corresponds to value <val> by retrieving the last occurence of `n before position X (which is the beginning of the <val> string) than parsing each line in a loop or using the dreaded regular expressions (which I hate profoundly).
Unfortunately now it's way too late for such a change. Maybe in an additional function.
Part of my AHK work can be found here.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: InStr() with negative occurence

12 Jul 2017, 08:26

A bit more InStr:

Code: Select all

q:: ;nth/nth-to-last match starting at a point
vText := "a a a a a"
MsgBox, % InStr(vText, "a", 0, 1, 3) ;5 ;3rd match (start at 1)
MsgBox, % InStr(vText, "a", 0, 2, 3) ;7 ;3rd match (start at 2)
MsgBox, % InStr(vText, "a", 0, 3, 3) ;7 ;3rd match (start at 3)
MsgBox, % InStr(vText, "a", 0, 4, 3) ;9 ;3rd match (start at 4)

vIsV1 := !!SubStr(1,0)
MsgBox, % InStr(vText, "a", 0, vIsV1-1, 3) ;5 ;3rd-to-last match (start at -1)
MsgBox, % InStr(vText, "a", 0, vIsV1-2, 3) ;3 ;3rd-to-last match (start at -2)
MsgBox, % InStr(vText, "a", 0, vIsV1-3, 3) ;3 ;3rd-to-last match (start at -3)
MsgBox, % InStr(vText, "a", 0, vIsV1-4, 3) ;1 ;3rd-to-last match (start at -4)
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: InStr() with negative occurence

12 Jul 2017, 10:36

I do not get this. Is this a feature request or a syntax request? If you specify InStr(hay,ned,, startPos:=0) you get the last match by finding the first match from the end, and stopping the search there. Should InStr(hay,ned,,1,-1) mean you find the same position but by going through the whole string from the beginning to the end?
a method to find the last occurence of string N before position X.
Maybe something like this?

Code: Select all

hay:="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
lengthOfOnePart:=strlen(hay)
hay.=hay hay
N:="adipiscing"
MsgBox, % FindLastBeforePos(hay,N,,lengthOfOnePart*2) " " Instr(hay,N,,,2) 

FindLastBeforePos(hay,ned,case:=false, pos:=0,occurrence:=1){
	; "Last" can be changed via occurrence.
	return InStr(hay,ned,case,-(StrLen(hay)-pos),occurrence)
}
Cheers.
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: InStr() with negative occurence

12 Jul 2017, 11:09

Well, kinda like that:

Code: Select all

hay:="item1=val1`nitem2=val2`nitem3=val3`nitem4=val4"
lengthOfOnePart:=strlen(hay)
N:="`n"
X := InStr(hay, "val3")
r := FindLastBeforePos(hay,N,,X)
msgbox, % SubStr(hay, r+1, x-r-2) 

FindLastBeforePos(hay,ned,case:=false, pos:=0,occurrence:=1){
	; "Last" can be changed via occurrence.
	return InStr(hay,ned,case,-(StrLen(hay)-pos),occurrence)
}
Sure StrLen(hay)-pos is a working hack but it would've been a tad easier to just have InStr(hay,ned,case,-X) find it directly.

Code: Select all

; EXAMPLE (compatible AHK 1.0, no 'occurence' parameter)
s := "i1=v1`ni2=v2`ni3=v3`ni4=v4`ni5=v5", f := "`n", w := "v4"
p := InStr2(s, w, 0), r := InStr2(s, f, 0, -p), msg := Substr(s, r+1, p-r-2)
msgbox, p=%p% r=%r%`nresult=%msg%

InStr2(h, n, c=0, p=1, o=1)
{
if (p < 0)
	return InStr(SubStr(h, 1, Abs(p)), n, c, 0)
return InStr(h, n, c, p)
}
Nevertheless we'll have to work with what we have and hopefully I'll remember the above solution for the future. Thanks. :)
Part of my AHK work can be found here.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: InStr() with negative occurence

12 Jul 2017, 15:57

OK, here's InStr as I always envisaged it, and as a bonus, 'SubStrRange', which I've been 50/50 on for a long time, whether it should be a default function.

Code: Select all

w:: ;test 'SubStrRange'
vText := "abcdefghijklmnopqrstuvwxyz"
MsgBox, % JEE_SubStrRange(vText, 6, 10) ;fghij
MsgBox, % JEE_SubStrRange(vText, 6) ;fghijklmnopqrstuvwxyz
return

;==================================================

q:: ;test 'InStrEx'
vText := "a a a a a"
vOcc := 1
;vOcc := 2

;note: values in comments are values when vOcc = 1
MsgBox, % JEE_InStrEx(vText, "a", 0, 1, vOcc) ;1
MsgBox, % JEE_InStrEx(vText, "a", 0, 2, vOcc) ;3
MsgBox, % JEE_InStrEx(vText, "a", 0, 3, vOcc) ;3

MsgBox, % JEE_InStrEx(vText, "a", 0, -3, vOcc) ;7
MsgBox, % JEE_InStrEx(vText, "a", 0, -2, vOcc) ;9
MsgBox, % JEE_InStrEx(vText, "a", 0, -1, vOcc) ;9

MsgBox, % JEE_InStrEx(vText, "a", 0, 1, -vOcc) ;1
MsgBox, % JEE_InStrEx(vText, "a", 0, 2, -vOcc) ;1
MsgBox, % JEE_InStrEx(vText, "a", 0, 3, -vOcc) ;3

MsgBox, % JEE_InStrEx(vText, "a", 0, -3, -vOcc) ;7
MsgBox, % JEE_InStrEx(vText, "a", 0, -2, -vOcc) ;7
MsgBox, % JEE_InStrEx(vText, "a", 0, -1, -vOcc) ;9
return

;==================================================

;same as InStr with some slight differences:
;- vPos specifies the start position, but not the search direction
;e.g. vPos := 3 means search from 3rd char (forwards/backwards depending on vOcc)
;e.g. vPos := -3 means search from 3rd-to-last char (forwards/backwards depending on vOcc)
;- AHK v2 style is used, so: -1 means last char, -2 means 2nd-to-last char
;- vOcc specificies the occurrence, and the search direction
;use positive vOcc to search forwards, use negative vOcc to search backwards
;e.g. vOcc := 3 means 3rd occurrence at/after position (search forwards)
;e.g. vOcc := -3 means 3rd occurrence at/before position (search backwards)
JEE_InStrEx(vText, vNeedle, vCaseSen=0, vPos=1, vOcc=1)
{
	if (vPos = 0) || (vOcc = 0)
		return ""
	vIsV1 := !!SubStr(1,0)
	if (vOcc < 0) && (vPos > 0)
		vPos := - StrLen(vText) - !vIsV1 + vPos
	else if (vOcc > 0) && (vPos < 0)
		vPos := StrLen(vText) + vIsV1 + vPos
	else if (vOcc < 0) && (vPos < 0)
		vPos += vIsV1
	return InStr(vText, vNeedle, vCaseSen, vPos, Abs(vOcc))
}

;==================================================

;specify first and last chars of text to retrieve
JEE_SubStrRange(vText, vPos1, vPos2="")
{
	if (vPos2 = "")
		return SubStr(vText, vPos1)
	else
		return SubStr(vText, vPos1, vPos2-vPos1+1)
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: InStr() with negative occurence

13 Jul 2017, 08:42

That SubStrRange() would definitely be a useful shortcut. Seeing it I got bit by nostalgia from good ol' Z80 BASIC days: LET b$ = a$(x TO y) with variants LET b$ = a$(x TO), LET b$ = a$(TO y) and LET b$ = a$(x). :)
Part of my AHK work can be found here.
joefiesta
Posts: 494
Joined: 24 Jan 2016, 13:54
Location: Pa., USA

Re: InStr() with negative occurence

08 Sep 2017, 10:18

Looking for something starting at the end of a string made me think of reversing a string, looking from the beginning and reversing the result. A lot of reversing, and probably a horribly slow way to find something.

But, what prompts my response is SUBSTRRANGE. Since AHK has no REVERSE() function--at least one language I know (REXX) does--an interesting use of SUBSTRRANGE could be reversing.


a := "abcdefghijklmnopqrstuvwxyz"
b := substrrange(a,10,5)

b would now be: JIHGFE

yes, if REGEX does this, that's wonderful. But, I think learning REGEX is the equivalent of 4 years in medical school. At 66, I don't see myself becoming a REGEX guru. (It is, admittedly, truly awesomely powerful, however!)

Joe Petree
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: InStr() with negative occurence

08 Sep 2017, 11:25

joefiesta, although a bit off topic, here are simple reverse functions,

Code: Select all

str:="..."
strrev(str)
msgbox % str
msgbox % strrevcpy(str) "`n" str

strrev(byref str){
	return DllCall("MSVCRT.DLL\" . (A_IsUnicode ? "_wcsrev": "_strrev"), "ptr", &str, "ptr")
}
strrevcpy(str){
	return StrGet(DllCall("MSVCRT.DLL\" . (A_IsUnicode ? "_wcsrev": "_strrev"), "ptr", &str, "ptr"))
}
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: InStr() with negative occurence

08 Sep 2017, 20:29

That's an interesting idea for a SubStrRange function. It's interesting all these different classic string functions that aren't uniformly available across programming languages.

After I tried collecting various key RegEx scripts I'd found, and had a go at working out a few others, I acquired a critical mass of RegEx knowledge that made it seem relatively straightforward, culminating in this:
jeeswg's RegEx tutorial (RegExMatch, RegExReplace) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=28031

[EDIT:] I meant to say that it seems that you can do virtually anything in a RegEx one-liner, apart from repeating a string n times, and now I've found out, apart from reversing a string. However, not that there isn't some special workaround somehow.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Wish List”

Who is online

Users browsing this forum: No registered users and 38 guests