VBspeed / VB6 to VB5 / InStrRev
VBspeed © 2000-10, updated: 05-Oct-2002
InStrRev
See also InStr


The Definition
Function InStrRev
Returns the position of an occurrence of one string within another, from the end of string.
Native to VB6, but not to VB5.
Declaration:
InStrRev(sCheck, sMatch[, Start[, Compare]])
Arguments:
sCheckRequired. String expression being searched.
sMatchRequired. String expression being searched for.
StartOptional. Numeric expression that sets the starting position for each search (ie, looks from here left-bound; note that for a match the last char of sMatch must be at pos <= Start).
If omitted, -1 is used, which means that the search begins at the last character position.
CompareOptional. Numeric value indicating the kind of comparison to use when evaluating substrings. If omitted, a binary comparison is performed.
Return Values:
Typical case:
InStrRev("abb", "b")           =>  3

Special cases:
sCheck is zero-length          =>  0
sMatch is zero-length          =>  start if start <= Len(sCheck), else 0
sMatch is not found            =>  0
Start < Len(sMatch)            =>  0
  (note: MSDN incorrectly states "Start > Len(sMatch) => 0", this is a typo!)
Remarks:
All InStrRev emulations I've seen use the built-in InStr function in one of the following principal ways to go about the problem:
 (a) search from left to right until InStr returns 0, or
 (b) search from right to left until InStr returns > 0, or
 (c) do a binary search iteratively scanning halved intervals (see InStrRev03/06)
As you see in the charts below, all strategies have their merits depending on the nature of the input parameters. So, if performance is critical and you can make assumptions about the input, you should choose the most suitable algorithm for your specific purpose beforehand.
The charts also reveal, that VB6's native InStrRev goes for strategy (b) since it's significantly weak when the right-most occurrence is far from the right end of the checked string.
Roll your own
If you want to have a go at InStrRev yourself, use this function (VB5/6-compatible) to verify the correctness of your code.


The Charts
Calls
  lRet = InStrRev(sCheck, sMatch, , Compare)
Call 1 sCheck = "http://www.xbeat.net/vbspeed/index.htm"
sMatch = "."
Call 2 sCheck = "http://www.xbeat.net/vbspeed/index.htm"
sMatch = "/"
Call 3 sCheck = "http://www.xbeat.net/vbspeed/index.htm"
sMatch = "w"
Call 4 sCheck = "http://www.xbeat.net/vbspeed/index.htm"
sMatch = "W"
Compare = vbTextCompare
Call 5 sCheck = "http://www.xbeat.net/vbspeed/index.htm"
sMatch = "www"
Call 6 sCheck = "http://www.xbeat.net/vbspeed/index.htm"
sMatch = "WWW"
Compare = vbTextCompare
Call 7 sCheck = Space$(99) & String$(99, "x") & Space(999)
sMatch = "x"  [many occurrences, but very far from right end]
Call 8 sCheck = Space$(100)
sMatch = "x"  [no occurrences]
 VB5
CodeAuthorDopingNotes
InStrRevVB6  
InStrRev01 Donald  
InStrRev02 Donald  
InStrRev03 Jost  
InStrRev06 JostAPI 
InStrRev07 GuyAPI 
InStrRev08 MarzoAPI 
Call 1
   
55.810.320Ás
24.350.240Ás
35.310.293Ás
65.850.323Ás
45.370.296Ás
11.000.055Ás
Call 2
   
54.340.375Ás
66.410.555Ás
44.030.348Ás
23.970.343Ás
34.020.347Ás
11.000.086Ás
Call 3
   
22.050.318Ás
612.611.960Ás
43.110.483Ás
54.000.623Ás
32.940.457Ás
11.000.155Ás
Call 4
   
428.765.090Ás
6206.7636.595Ás
543.657.726Ás
38.871.569Ás
23.710.656Ás
11.000.177Ás
Call 5
   
21.330.251Ás
610.531.997Ás
42.530.479Ás
53.010.572Ás
32.530.479Ás
11.000.190Ás
Call 6
   
410.012.721Ás
6138.7437.729Ás
524.796.743Ás
35.671.542Ás
22.860.777Ás
11.000.272Ás
Call 7
   
52.997.864Ás
6274.09720.633Ás
32.546.674Ás
11.002.629Ás
42.626.894Ás
21.403.671Ás
Call 8
   
21.040.203Ás
657.9611.357Ás
11.000.196Ás
31.200.236Ás
54.820.944Ás
41.800.353Ás
 VB6
CodeAuthorDopingNotes
InStrRev VB6  
InStrRev01 Donald  
InStrRev02 Donald  
InStrRev03 Jost  
InStrRev06 JostAPI 
InStrRev07 GuyAPI 
InStrRev08 MarzoAPI 
Call 1
22.990.169Ás
65.190.293Ás
34.200.237Ás
54.990.282Ás
75.580.315Ás
44.660.263Ás
11.000.056Ás
Call 2
23.200.266Ás
44.200.349Ás
76.840.569Ás
64.230.352Ás
54.200.349Ás
33.770.313Ás
11.000.083Ás
Call 3
53.960.555Ás
22.080.291Ás
713.851.940Ás
43.320.465Ás
64.590.643Ás
33.070.430Ás
11.000.140Ás
Call 4
412.821.963Ás
533.585.144Ás
7242.1237.090Ás
650.357.713Ás
310.461.602Ás
24.180.640Ás
11.000.153Ás
Call 5
52.780.569Ás
21.250.255Ás
710.072.063Ás
42.410.494Ás
63.070.628Ás
32.280.467Ás
11.000.205Ás
Call 6
47.592.045Ás
510.262.762Ás
7140.5737.856Ás
624.946.717Ás
35.981.609Ás
22.810.756Ás
11.000.269Ás
Call 7
65.1215.214Ás
52.457.275Ás
7243.40722.900Ás
42.246.640Ás
11.002.970Ás
32.146.343Ás
21.033.058Ás
Call 8
68.191.625Ás
21.030.204Ás
758.0011.509Ás
11.000.198Ás
31.350.267Ás
54.290.852Ás
41.480.294Ás
Conclusions
Sep-2000: The ranking is highly dependent on the type of call, ie. the makeup of the input parameters. It's hard to say what's the overall best emulation.
Note, how InStrRev02 and also VB6's InStrRev are weak when the right-most occurrence is far from the right end of the checked string (calls 3-5, see also Remarks above).
Oct-2002: The picture is somewhat clearer now ...
Mail your code! How to read all those numbers


The Code
InStrRev01
submitted 17-Sep-2000 by Donald Lessau  
Doping: none
Public Function InStrRev01( _
                            sCheck As String, _
                            sMatch As String, _
             Optional ByVal Start As Long = -1, _
                   Optional Compare As VbCompareMethod = vbBinaryCompare) As Long
' by Donald, donald@xbeat.net, rev. 001, 20000923
' strategy: search left to right until no more
  Dim lenSearchFor As Long
  Dim posFound As Long
  
  lenSearchFor = Len(sMatch)
  
  If lenSearchFor Then
    If Start <= 0 Then
      Start = Len(sCheck)
    End If
    Do
      posFound = InStr(InStrRev01 + 1, sCheck, sMatch, Compare)
      If posFound And (posFound + lenSearchFor - 1 <= Start) Then
        ' match
        InStrRev01 = posFound
      Else
        ' no more
        Exit Function
      End If
      ' condition not necessary
    Loop
  Else
    ' as VB6 InStrRev
    If Start <= Len(sCheck) Then
      InStrRev01 = Start
    End If
  End If

End Function
Author's comments:
Donald's comments:

top | charts


InStrRev02
submitted 23-Sep-2000 by Donald Lessau  
Doping: none

Public Function InStrRev02( _
                            sCheck As String, _
                            sMatch As String, _
             Optional ByVal Start As Long = -1, _
                   Optional Compare As VbCompareMethod = vbBinaryCompare) As Long
' by Donald, donald@xbeat.net, 20000923
' strategy: search right to left until found
  Dim lenSearchFor As Long
  Dim posFound As Long
  Dim posSearch As Long
 
  lenSearchFor = Len(sMatch)
  
  If lenSearchFor Then
    If Start <= 0 Then
      Start = Len(sCheck)
    End If
    For posSearch = Start - lenSearchFor + 1 To 1 Step -1
      posFound = InStr(posSearch, sCheck, sMatch, Compare)
      If posFound Then
        If (posFound + lenSearchFor - 1) <= Start Then
          InStrRev02 = posFound
          Exit Function
        End If
      End If
    Next
  Else
    ' as VB6 InStrRev
    If Start <= Len(sCheck) Then
      InStrRev02 = Start
    End If
  End If
  
End Function
Author's comments:
Donald's comments:

top | charts


InStrRev03
submitted 24-Nov-2000 by Jost Schwider    vb-tec.de
Doping: none
Public Static Function InStrRev03( _
    ByRef sCheck As String, _
    ByRef sMatch As String, _
    Optional ByVal Start As Long, _
    Optional ByVal Compare As VbCompareMethod = vbBinaryCompare, _
    Optional ByVal Stopp As Long = 1 _
  ) As Long
' by Jost Schwider, jost@schwider.de, 20001124
  
  Dim Index As Long
  Dim Pivot As Long
  
  If Len(sMatch) Then
  
    'Linke Grenze bestimmen:
    Stopp = InStr(Stopp, sCheck, sMatch, Compare)
    If Stopp = 0 Then Exit Function
    
    'Rechte Grenze bestimmen:
    If Start <= 0 Then
      Start = Len(sCheck) - Len(sMatch) + 1
    Else
      Start = Start - Len(sMatch) + 1
    End If
    If Stopp > Start Then Exit Function
    
    'Ersten Treffer merken:
    InStrRev03 = Stopp
    Stopp = Stopp + 1
    
    'Binńre Suche / Intervall-Halbierungs-Verfahren:
    Do
      'Ab Mitte testen:
      Pivot = (Stopp + Start) \ 2
      Index = InStr(Pivot, sCheck, sMatch, Compare)
      
      'Treffer?
      If Index Then
        If Index > Start Then
          'ungŘltiger Treffer:
          Start = Pivot - 1
        Else
          InStrRev03 = Index
          Stopp = Index + 1
        End If
      Else
        Start = Pivot - 1
      End If
    Loop Until Stopp > Start
  
  Else
    If Start <= Len(sCheck) Then InStrRev03 = Start
  End If

End Function
Author's comments:
Donald's comments:

top | charts


InStrRev06
submitted 18-Dec-2000 by Jost Schwider    vb-tec.de
Doping: API
Private Declare Sub PokeLng Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByVal Addr As Long, Value As Long, _
    Optional ByVal Bytes As Long = 4)

Public Function InStrRev06( _ ByRef sCheck As String, _ ByRef sMatch As String, _ Optional ByVal Start As Long, _ Optional ByVal Compare As VbCompareMethod = vbBinaryCompare _ ) As Long ' by Jost Schwider, jost@schwider.de, 20001218 Dim Stopp As Long Dim Index As Long Dim Pivot As Long Dim Length As Long Dim LengthPtr As Long Dim MatchLen As Long If Compare = vbBinaryCompare Then MatchLen = LenB(sMatch) - 1 If MatchLen > -1 Then 'Linke Grenze bestimmen: Stopp = InStrB(sCheck, sMatch) If Stopp = 0 Then Exit Function 'Rechte Grenze bestimmen: Length = LenB(sCheck) If Start <= 0 Then Start = Length - MatchLen LengthPtr = StrPtr(sCheck) - 4 Else Start = Start + Start - MatchLen If Stopp > Start Then Exit Function LengthPtr = StrPtr(sCheck) - 4 PokeLng LengthPtr, Start + MatchLen End If 'Ersten Treffer merken: InStrRev06 = Stopp Stopp = Stopp + 2 'Binńre Suche / Intervall-Halbierungs-Verfahren: Do 'Ab Mitte testen: Pivot = (Stopp + Start) \ 2 Index = InStrB(Pivot, sCheck, sMatch) 'Treffer? If Index Then InStrRev06 = Index If Index >= Start Then PokeLng LengthPtr, Length InStrRev06 = InStrRev06 \ 2 + 1 Exit Function End If Stopp = Index + 2 Else If Stopp + 8 >= Pivot Then Exit Do Start = Pivot - 1 PokeLng LengthPtr, Start + MatchLen End If Loop 'Konventionell weiter machen: Index = InStrB(Stopp, sCheck, sMatch) Do While Index InStrRev06 = Index Index = InStrB(Index + 2, sCheck, sMatch) Loop InStrRev06 = InStrRev06 \ 2 + 1 PokeLng LengthPtr, Length Else If Start <= Len(sCheck) Then InStrRev06 = Start End If Else InStrRev06 = InStrRev06(LCase$(sCheck), LCase$(sMatch), Start) End If End Function
Author's comments:
Donald's comments: Replaces previous submissions InStrRev04 and InStrRev05 which both had a problem with large strings.

top | charts


InStrRev07
submitted 22-Nov-2001 by Guy Gervais  
Doping: API (cf. Dope'n'Declarations)
The code is rather long, have a look here.
Author's comments:
Donald's comments:

top | charts


InStrRev08
submitted 02-Oct-2002 by Marzo Junior  
Doping: TLB (cf. Dope'n'Declarations)
Class-wrapped. The class, which also includes a bunch of related functions, is waiting for you here.
Author's comments:
Donald's comments:

top | charts




VBspeed © 2000-10 by Donald Lessau