DAY 032: _INSTRREV
#1
_INSTRREV is an INSTR function, but in reverse! Instead of searching left to right, it searches from right to left.

SYNTAX _INSTRREV(seed%, string$, search$)

Where:

seed% is starting point in the string to begin the search.
string$ is the string to be searched.
search$ is the search term.

Note: The first parameter, seed%, is optional. We can also code it as: _INSTRREV(string$, search$)

Use: Parsing function.

So if we can already search a string forward with INSTR(), what's the benefit of using _INSTRREV()?

Glad you asked...

Let's say we have a page margin of 40 spaces wide. Here is our first string for that document...

a$ = "Pete and Steve walk into a bar. Steve blames Pete for not raising the bar higher."

Oops, that's longer than 40-characters? What do we do now Batman?

Well, simple "Stevey-boy" Wonder, we use the Bat-o-axe and chop it! (I have an old bat-o-axe around the house... Ouch! Hit by the bat-pan again!)

Well while I recover from being being badly bat-tered, let's take a short commercial break and see INSTR() vs  _INSTRREV() in action.

Code: (Select All)
WIDTH 82, 25
LOCATE 10, 1: PRINT " Okay, here is our string to chop to the page width of 40...              "
a$ = "Pete and Steve walk into a bar. Steve blames Pete for not raising the bar higher."
LOCATE 20, 1: PRINT a$; ''PRINT MID$(a$, 1, _WIDTH - 2): PRINT " "; MID$(a$, _WIDTH - 1)
mr = 40 ' Right margin limit
LOCATE 1, mr + 1: PRINT "|";
SLEEP
LOCATE 10, 1: PRINT "First we chopped the string to the page width: a$ = MID$(a$, 1, mr)                  "
a$ = MID$(a$, 1, mr)
LOCATE 1, 1
PRINT a$;
SLEEP
LOCATE 10, 1: PRINT "Okay, we need to get rid of the "; CHR$(34); "bl"; CHR$(34); " part of blames on our first line...    "
LOCATE 12, 1: PRINT "So let's try doing that with INSTR() with a nice long loop function..."; ""
SLEEP
LOCATE 10, 1: PRINT "Well that's working, but it's taking several parsing loops.                                                        "
LOCATE 12, 1: PRINT SPACE$(_WIDTH);
LOCATE 1, 1: PRINT SPACE$(mr);
LOCATE 1, 1
seed% = 0: j = 0
DO
    chop = j
    j = INSTR(seed%, a$, " ")
    COLOR 8: PRINT MID$(a$, seed%, j - seed% + 1);: _DELAY .66 ' For fun, we will time delay print each parse.
    seed% = j + 1 ' Move forward in our string - character past the last space.
LOOP UNTIL j = 0
COLOR 7
LOCATE 1, 1
PRINT MID$(a$, 1, chop);
SLEEP
LOCATE 10, 1: PRINT "Okay, let's do that with a 1-line _INSTRREV(): chop = _INSTRREV(a$, "; CHR$(34); " "; CHR$(34); ") "
LOCATE 1, 1: PRINT SPACE$(mr);
SLEEP 5
chop = _INSTRREV(a$, " ")
LOCATE 1, 1
PRINT MID$(a$, 1, chop);
LOCATE 10, 1: PRINT "Well that was easy!                                                                        "


Now the seed% part in _INSTRREV is used a bit differently than INSTR() in that it is read right to left, instead of left to right. So instead of stating your seed% at zero, you start it at the last space - 1 in your string to be chopped.

Code: (Select All)
a$ = "Pete and Steve walk into a bar. Steve bl"
LOCATE 1, 41: PRINT "|";
_DELAY 1
DO
    seed% = _INSTRREV(a$, " ") - 1: j = 0
    DO
        chop = j
        j = _INSTRREV(seed%, a$, " ")
        LOCATE , j + 1: PRINT MID$(a$, j + 1, seed% - j + 1);: _DELAY .5
        REM PRINT seed%, j
        seed% = j - 1 ' Move backwards in our string - character past the previous space.
    LOOP UNTIL j = 0
    LOCATE 1, 1: PRINT SPACE$(40);: _DELAY 1: LOCATE 1, 1
    seed% = 0: j = 0
    DO
        chop = j
        j = INSTR(seed%, a$ + " ", " ")
        PRINT MID$(a$, seed%, j - seed% + 1);: _DELAY .5 ' For fun, we will time delay print each parse.
        seed% = j + 1 ' Move forward in our string - character past the last space.
    LOOP UNTIL j = 0
    LOCATE 1, 1: PRINT SPACE$(40);: _DELAY 1: LOCATE 1, 1
LOOP

Well one practical application I wish we could do with this keyword is seed it find a term in a list of terms, separated with a delimiter. Well, we can build a function and use our key INSTRREV() and I'll throw in _INSTR() for no extra charge.

First input if you are searching forwards or backwards and then input the what number term to find, 1 - 10.

Code: (Select All)
DO
    DO
        CLS
        a$ = "dog cat rabbit cow mule pig elephant tiger bear Steve"
        PRINT a$: PRINT
        INPUT "Pick From the Left [1] or the Right [-1]: ", tmp%: PRINT
        INPUT "From 1 - 10, What Term #", tnum%
        IF tnum% >= 1 AND tnum% <= 10 AND tmp% >= -1 AND tmp% <= 2 AND tmp% <> 0 THEN EXIT DO
    LOOP
    tnum% = tnum% * tmp%
    PRINT
    PRINT " You chose: "; parse(a$, tnum%)
    PRINT: PRINT "Press any key to continue or Esc to quit..."
    _KEYCLEAR
    SLEEP
    IF INKEY$ = CHR$(27) THEN SYSTEM
LOOP

FUNCTION parse$ (a$, tnum%)
    IF tnum% < 0 THEN
        seed% = _INSTRREV(a$ + " ", " ") - 1: j = 0
        FOR i = 1 TO ABS(tnum%)
            chop = j
            j = _INSTRREV(seed%, a$ + " ", " ")
            IF i <> ABS(tnum%) THEN seed% = j - 1
        NEXT
        parse$ = MID$(a$, j + 1, seed% - j + 1)
    ELSE
        seed% = 0: j = 0
        FOR i = 1 TO tnum%
            chop = j
            j = INSTR(seed%, a$ + " ", " ")
            IF i <> tnum% THEN seed% = j + 1
        NEXT
        parse$ = MID$(a$, seed%, j - seed% + 1)
    END IF
END FUNCTION

Other uses are parsing off a file path to show just the directory:

From the Wiki...

Code: (Select All)
fullPath$ = "C:\Documents and Settings\Administrator\Desktop\qb64\internal\c\libqb\os\win\libqb_1_2_000000000000.o"
file$ = MID$(fullPath$, _INSTRREV(fullPath$, "\") + 1)
PRINT file$


One last thing, which also applies to INSTR(). Both will return zero if the search term, or aka sub-string, isn't found. So if you are using it with mid$() be aware that zero will give you the full string in the output, instead of a null string. You will need to write a conditional statement to handle this behavior...

Code: (Select All)
a$ = "123456789" ' No space(s).
x$ = "" ' Make x$ a null string just in case x$ was assigned someplace else.
substring$ = " "
j = _INSTRREV(a$, substring$)
IF j THEN x$ = MID$(a$, j) ' Only change null x$ if j is non-zero.
PRINT "x$ = "; x$ ' Nothing will print here now that we put in the above condition.
SLEEP
PRINT: PRINT MID$(a$, _INSTRREV(a$, substring$)) ' This would have printed the whole string without the condition.

Tune in tomorrow. Same Bat time, same Bat channel.

Pete
Reply
#2
Nice guide, Pete.  So reversing a string and returning LEN(s$) - INSTR(s$, ...) is too much of an exercise that it warrants linguistic enhancement and a whole page but something more nuanced and conducive to optimization like a profound INPUT$ warrants a thesis on the pedagogy of doing it yourself
Reply
#3
This was badly needed in QuickBASIC, although many more years would pass before there were enough users of Linux that searching from the end of a string to the beginning for a "slosh" became necessary.

I have yet to see in somebody else's source code this function replicated by reversing a string, using "INSTR" and then creating a return integer offset. OTOH reversing a string required some performance penalty.

I had thought "FilenamePart" function in Purebasic was a "black box". (It returned only the eight characters or less of an "8-dot-3" MS-DOS-style filename, or the similar portion between the last "slosh" and the period that separates it from the suffix.) But what it did, wound up being used in a lot of my programs for that system. I worked on an emulation of that function for QB64. I wound up doing a "stringfieldseg" which does the "_INSTRREV" thing for either slash and then returns the rest of the string which is assumed to be a filename.

EDIT: Got ninja'ed by vince, and he was able to tell before me what I said in the second paragraph. :tu:
Reply
#4
I need help getting this to work

Code: (Select All)
deflng a-z

s$ = "much Steve go or after full Steve then if"
? s$

? instr(s$, "Steve")
? _instrrev(s$, "Steve")
? insteve(s$, "Steve")

function insteve(a$, b$)
    insteve = len(a$) - len(b$) + 2 - instr(resteve$(a$), resteve$(b$))
end function

function resteve$(a$)
    b$ = ""
    for i=1 to len(a$)
        b$ = b$ + mid$(a$, len(a$) - i + 1, 1)
    next
    resteve$ = b$
end function
Reply
#5
String = "fi neht evetS lluf retfa ro og evetS hcum"

SubString$ = "evetS"

You have two different instances of the SubString, so is your goal to count from left to right or right to left?

Well, I would look at it this way...

Code: (Select All)
$CONSOLE:ONLY
DEFLNG A-Z

LINE INPUT "Search term: "; p$

s$ = "much Steve go or after full Steve then if"
PRINT s$
PRINT INSTR(s$, p$)
PRINT _INSTRREV(s$, p$)
PRINT resteve$(s$)
j = insteve(s$, p$)
LOCATE , j: PRINT "^"
COLOR 14: PRINT j: COLOR 7, 0
SLEEP
RUN

FUNCTION insteve (a$, b$)
    insteve = INSTR(resteve$(a$), resteve$(b$))
END FUNCTION

FUNCTION resteve$ (a$)
    b$ = ""
    FOR i = 1 TO LEN(a$)
        b$ = b$ + MID$(a$, LEN(a$) - i + 1, 1)
    NEXT
    resteve$ = b$
END FUNCTION

Pete
If eggs are brain food, Biden takes his scrambled.
Reply
#6
don't hurt yourself, Pete, I was just trying to recreate _INSTRREV in QB45.  Goes to show we're no Steve to be messing with this stuff, I'm glad it was added into PE
Reply
#7
Oh, that's what you were shooting for. Okay, maybe you were just over-thinking it?

Code: (Select All)
a$ = "much Steve go or after full Steve then if"
PRINT a$: PRINT

DO
    LOCATE 10, 1: PRINT SPACE$(_WIDTH);
    LOCATE 10, 1: LINE INPUT "Search term: "; b$
    j = instrrev&(seed&, a$, b$)
    PRINT: PRINT "INSTRREV ="; _INSTRREV(seed&, a$, b$), "Function ="; j
    LOCATE 2: PRINT SPACE$(_WIDTH);
    IF j THEN LOCATE 2, j: PRINT "^"
LOOP

FUNCTION instrrev& (seed&, strng$, srch$)
    b$ = SPACE$(LEN(srch$))
    FOR i& = LEN(b$) TO 1 STEP -1
        j& = j& + 1
        MID$(b$, j&, 1) = MID$(srch$, i&, 1)
    NEXT
    j& = 0
    a$ = SPACE$(LEN(strng$))
    FOR i& = LEN(a$) TO 1 STEP -1
        j& = j& + 1
        MID$(a$, j&, 1) = MID$(strng$, i&, 1)
    NEXT
    IF INSTR(seed&, a$, b$) THEN
        instrrev& = LEN(a$) - LEN(b$) - (INSTR(seed&, a$, b$) - 2)
    ELSE
        instrrev& = 0
    END IF
END FUNCTION

I get the two Steve's now. It needs to pick out the second one to be _INSTRREV() compliant. Good thinking!

Pete
Reply
#8
beautiful code, reminiscent of a young Steve
Reply
#9
(12-13-2022, 02:02 AM)vince Wrote: beautiful code, reminiscent of a young Steve

Did someone slip some viagra, or something, to vince?  Man, he's really been obsessing over Steve for a while now!
Reply
#10
Here's about the simplest and easiest example I can imagine for use with _INSTRREV:

Code: (Select All)
a$ = "This is a string of text for testing."
Print a$

Print "INSTR demo"
Do
    p = InStr(p + 1, a$, "i")
    If p Then Print "i found at position #"; p
Loop Until p = 0
Print
Print
Print "_INSTRREV demo"
p = Len(a$)
Do
    p = _InStrRev(p - 1, a$, "i")
    If p Then Print "i found at position #"; p
Loop Until p = 0


Start at the end of your text, work backwards, until you're at the front of your text.  It's just that simple. If you can use INSTR, then you shouldn't have any issues using INSTRREV.  Wink
Reply




Users browsing this thread: 8 Guest(s)