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