home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 6_2008-2009.ISO / data / zips / Fast_Sub-S2169531272009.psc / SubStrCount / SubStrCntApi.bas < prev    next >
BASIC Source File  |  2009-11-28  |  13KB  |  351 lines

  1. Attribute VB_Name = "mSubStrCntApi"
  2. Option Explicit
  3.  
  4. ' ñññññññññññññññññññññññññññññññññññññññññññññññññññññññññ-⌐Rd-ñ
  5. '
  6. '                        Sub-String Count
  7. '
  8. '  This function searches the passed string for occurences of the
  9. '  specified sub-string. It has the ability to make case-sensitive
  10. '  or case-insensitive searches.
  11. '
  12. '  It can also perform whole-word-only matching using a unique
  13. '  delimiter function included in this module.
  14. '
  15. '  SubStringCount will return the number of matches or zero if none.
  16. '
  17. '                     Extended Functionality
  18. '
  19. '  This SubStringCount implementation offers extended functionality
  20. '  through the use of the optional lHitLimit parameter. This allows
  21. '  it to be used in a similar way to other token style functions.
  22. '
  23. '  By passing the lHitLimit parameter as any positive value allows
  24. '  you to limit how many matches are found in the current call, and
  25. '  the value of the lStartPos parameter *is modified* to identify the
  26. '  start position in the search string of the last sub-string found
  27. '  (or zero if none found).
  28. '
  29. '  In this case, the function will return a value equal to or less
  30. '  than the value of the lHitLimit parameter, and zero if none found.
  31. '
  32. '  Using this feature you can limit the number of matches found, and
  33. '  make subsequent calls to SubStringCount by passing lStartPos + 1
  34. '  (or lStartPos + Len(sSubStr)) to step through the search process
  35. '  as needed, and stop when the function returns zero.
  36. '
  37. '                        Whole-Word-Only
  38. '
  39. '  By default, all non-alphabetic characters (with the exception of
  40. '  underscores) are automatically treated as word delimiters when
  41. '  performing whole-word-only seaches and do not need to be specified.
  42. '
  43. '  As only alphabetic characters are treated as non-delimiters you
  44. '  can specify custom non-delimiters, that is, any character(s) can
  45. '  be specified as part of whole words and therefore be treated as
  46. '  non-delimiters.
  47. '
  48. '  To make numerical characters part of whole words and so set
  49. '  as non-delimiters *by default* add this line to the IsDelim
  50. '  function's select case statement:
  51. '      Case 48 To 57: IsDelim = False
  52. '
  53. '  To specify custom/run-time changes to the list of delimiters make
  54. '  a call to the public SetDelim subroutine and add character(s) to
  55. '  be handled as part of whole words (or as delimiters):
  56. '      SetDelim "1234567890", False
  57. '
  58. '  Remember, all non-alphabetic characters are already treated as
  59. '  word delimiters and so do not need to be specified through a
  60. '  call to SetDelim ???, True. Also, alphabetic characters can be
  61. '  treated as word delimiters through a call to SetDelim "a", True.
  62. '
  63. '  Most delimiter implementations build a list/array to hold all
  64. '  delimiters, but this modules approach is *much* faster.
  65. '
  66. '                            Notes
  67. '
  68. '  Passing lStartPos with a value < 1 will not cause an error; it
  69. '  will default to 1 and start the search at the first character in
  70. '  the search string.
  71. '
  72. '  The lStartPos parameter will be reset appropriately if lHitLimit
  73. '  is specified > zero, but will be *left unchanged* if lHitLimit
  74. '  is omitted or passed with a value <= zero.
  75. '
  76. '                          Free Usage
  77. '
  78. '  You may use this code in any way you wish, but please respect
  79. '  my copyright. But, if you can modify this function in some way
  80. '  to speed it up or to add extra features then you can claim it
  81. '  as your own!
  82. '
  83. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  84.  
  85. ' Declare some CopyMemory Alias's (thanks Bruce :)
  86. Private Declare Sub CopyMemByV Lib "kernel32" Alias "RtlMoveMemory" (ByVal lpDest As Long, ByVal lpSrc As Long, ByVal lByteLen As Long)
  87. Private Declare Sub CopyMemByR Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal lByteLen As Long)
  88.  
  89. Private lDelimList As Long
  90. Private lNotDelim As Long
  91.  
  92. Private laDelim() As Long
  93. Private laNotDel() As Long
  94.  
  95. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  96. '       Function to search for occurences of a sub-string.
  97. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  98.  
  99. Public Function SubStringCount(sStr As String, _
  100.                                sSubStr As String, _
  101.                                Optional lStartPos As Long = 1, _
  102.                                Optional ByVal lCompare As _
  103.                                VbCompareMethod = vbBinaryCompare, _
  104.                                Optional ByVal bWordOnly As Boolean, _
  105.                                Optional ByVal lHitLimit As Long _
  106.                                ) As Long ' ⌐Rd
  107.  
  108.     Dim sStrV As String, sSubStrV As String
  109.     Dim lLenStr As Long, lLenSub As Long
  110.     Dim lBefore As Long, lAfter As Long
  111.     Dim lStart As Long, lHit As Long
  112.     Dim lOffStart As Long, bDelim As Boolean
  113.  
  114.     On Error GoTo FreakOut
  115.  
  116.     lLenStr = Len(sStr)
  117.     If (lLenStr = 0) Then Exit Function ' No text
  118.  
  119.     lLenSub = Len(sSubStr)
  120.     If (lLenSub = 0) Then Exit Function ' Nothing to find
  121.  
  122.     If lStartPos < 1 Then lHit = 1 Else lHit = lStartPos ' Validate start pos
  123.  
  124.     If (lCompare = vbTextCompare) Then
  125.         ' Better to convert once to lowercase than on every call to InStr
  126.         sSubStrV = LCase$(sSubStr): sStrV = LCase$(sStr)
  127.     Else
  128.         CopyMemByV VarPtr(sSubStrV), VarPtr(sSubStr), 4&
  129.         CopyMemByV VarPtr(sStrV), VarPtr(sStr), 4&
  130.     End If
  131.  
  132.     lHit = InStr(lHit, sStrV, sSubStrV, vbBinaryCompare)
  133.  
  134.     Do While (lHit)           ' Do until no more hits
  135.  
  136.         If bWordOnly = False Then
  137.  
  138.             lStart = lHit
  139.             SubStringCount = SubStringCount + 1
  140.             If (SubStringCount = lHitLimit) Then Exit Do
  141.  
  142.             lOffStart = lLenSub ' Offset next start pos
  143.         Else
  144.             lOffStart = 1 ' Default offset start pos
  145.  
  146.             lBefore = lHit - 1
  147.             If (lBefore = 0) Then
  148.                 bDelim = True
  149.             Else
  150.                 bDelim = IsDelimI(MidI(sStrV, lBefore))
  151.             End If
  152.  
  153.             If bDelim Then
  154.  
  155.                 lAfter = lHit + lLenSub
  156.                 If (lAfter > lLenStr) Then
  157.                     bDelim = True
  158.                 Else
  159.                     bDelim = IsDelimI(MidI(sStrV, lAfter))
  160.                 End If
  161.  
  162.                 If bDelim Then
  163.  
  164.                     lStart = lHit
  165.                     SubStringCount = SubStringCount + 1
  166.                     If (SubStringCount = lHitLimit) Then Exit Do
  167.  
  168.                     lOffStart = lLenSub ' Offset next start pos
  169.                 End If
  170.             End If
  171.         End If
  172.  
  173.         lHit = InStr(lHit + lOffStart, sStrV, sSubStrV)
  174.     Loop
  175.  
  176.     If (lHitLimit > 0) Then lStartPos = lStart
  177. FreakOut:
  178.     If (lCompare = vbBinaryCompare) Then
  179.         CopyMemByR ByVal VarPtr(sSubStrV), 0&, 4& ' De-reference pointer
  180.         CopyMemByR ByVal VarPtr(sStrV), 0&, 4&    ' De-reference pointer
  181.     End If
  182. End Function
  183.  
  184. ' ññ IsDelim ñññññññññññññññññññññññññññññññññññññññññññññññññññññ
  185. '
  186. '  This function is called by the Replace function during a
  187. '  whole word only procedure. You can also call this function
  188. '  from your own code - very handy for string parsing.
  189. '
  190. '  It checks if the character passed is a common word delimiter,
  191. '  and then returns True or False accordingly.
  192. '
  193. '  By default, any non-alphabetic character (except for an
  194. '  underscore) is considered a word delimiter, including numbers.
  195. '
  196. '  By default, an underscore is treated as part of a whole word,
  197. '  and so is not considered a word delimiter in whole word only
  198. '  searches.
  199. '
  200. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  201.  
  202. Public Function IsDelim(Char As String) As Boolean ' ⌐Rd
  203.     Dim iIdx As Long
  204.     Dim iAscW As Long
  205.     iAscW = AscW(Char)
  206.     Select Case iAscW
  207.         ' Uppercase, Underscore, Lowercase chars not delimiters
  208.         Case 65 To 90, 95, 97 To 122: IsDelim = False
  209.  
  210.         'Case 39, 146: IsDelim = False  ' Apostrophes not delimiters
  211.         'Case 48 To 57: IsDelim = False ' Numeric chars not delimiters
  212.  
  213.         Case Else: IsDelim = True ' Any other character is delimiter
  214.     End Select
  215.     If IsDelim And (lNotDelim <> 0) Then
  216.         Do Until iIdx = lNotDelim
  217.             If laNotDel(iIdx) = iAscW Then Exit Do
  218.             iIdx = iIdx + 1
  219.         Loop
  220.         If (iIdx < lNotDelim) Then
  221.             IsDelim = False
  222.             ' SetDelim doesn't allow chars to repeat
  223.             ' in both lists so we can exit
  224.             Exit Function
  225.         End If
  226.     End If
  227.     If (IsDelim = False) And (lDelimList <> 0) Then
  228.         ' May need alphabetic characters to behave as delimiters
  229.         Do Until iIdx = lDelimList
  230.             If laDelim(iIdx) = iAscW Then Exit Do
  231.             iIdx = iIdx + 1
  232.         Loop
  233.         IsDelim = iIdx < lDelimList
  234.     End If
  235. End Function
  236.  
  237. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  238.  
  239. Public Function IsDelimI(ByVal iAscW As Long) As Boolean ' ⌐Rd
  240.     Dim iIdx As Long
  241.     Select Case iAscW
  242.         ' Uppercase, Underscore, Lowercase chars not delimiters
  243.         Case 65 To 90, 95, 97 To 122: IsDelimI = False
  244.  
  245.         'Case 39, 146: IsDelim = False  ' Apostrophes not delimiters
  246.         'Case 48 To 57: IsDelim = False ' Numeric chars not delimiters
  247.  
  248.         Case Else: IsDelimI = True ' Any other character is delimiter
  249.     End Select
  250.     If IsDelimI And (lNotDelim <> 0) Then
  251.         Do Until iIdx = lNotDelim
  252.             If laNotDel(iIdx) = iAscW Then Exit Do
  253.             iIdx = iIdx + 1
  254.         Loop
  255.         If (iIdx < lNotDelim) Then
  256.             IsDelimI = False
  257.             ' SetDelim doesn't allow chars to repeat
  258.             ' in both lists so we can exit
  259.             Exit Function
  260.         End If
  261.     End If
  262.     If (IsDelimI = False) And (lDelimList <> 0) Then
  263.         ' May need alphabetic characters to behave as delimiters
  264.         iIdx = 0
  265.         Do Until iIdx = lDelimList
  266.             If laDelim(iIdx) = iAscW Then Exit Do
  267.             iIdx = iIdx + 1
  268.         Loop
  269.         IsDelimI = iIdx < lDelimList
  270.     End If
  271. End Function
  272.  
  273. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  274.  
  275. Public Property Get MidI(sStr As String, ByVal lPos As Long) As Integer
  276.     CopyMemByR MidI, ByVal StrPtr(sStr) + lPos + lPos - 2, 2&
  277. End Property
  278.  
  279. Public Property Get MidIB(sStr As String, ByVal lPosB As Long) As Integer
  280.     CopyMemByR MidIB, ByVal StrPtr(sStr) + lPosB - 1, 2&
  281. End Property
  282.  
  283. ' ññ SetDelim ññññññññññññññññññññññññññññññññññññññññññññññññññññ
  284. '
  285. '  Specifies whether character(s) should be handled as delimiter
  286. '  in whole word only searches.
  287. '
  288. '  Remember, all non-alphabetic characters (with the exception of
  289. '  underscores) are already treated as word delimiters by default
  290. '  and do not need to be specified through SetDelim.
  291. '
  292. '  Note that multiple characters must not be seperated by spaces
  293. '  or any other character.
  294. '
  295. '  For example, to set all numeric charaters and apostrophes as
  296. '  part of whole words (non-delimiters):
  297. '
  298. '  SetDelim "1234567890'Æ", False
  299. '
  300. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  301.  
  302. Public Sub SetDelim(Char As String, Optional ByVal IsDelimiter As Boolean) ' ⌐Rd
  303.     On Error GoTo ErrHandler
  304.     Dim iPos As Long, iChr As Long
  305.     Dim idx1 As Long, idx2 As Long
  306.  
  307.     idx1 = Len(Char)
  308.     If IsDelimiter Then
  309.         ReDim Preserve laDelim(0 To lDelimList + idx1) As Long
  310.     Else
  311.         ReDim Preserve laNotDel(0 To lNotDelim + idx1) As Long
  312.     End If
  313.     For iPos = 1 To idx1
  314.         iChr = MidI(Char, iPos)
  315.         idx1 = 0
  316.         Do Until idx1 = lDelimList
  317.             If laDelim(idx1) = iChr Then Exit Do
  318.             idx1 = idx1 + 1
  319.         Loop
  320.         idx2 = 0
  321.         Do Until idx2 = lNotDelim
  322.             If laNotDel(idx2) = iChr Then Exit Do
  323.             idx2 = idx2 + 1
  324.         Loop
  325.         If IsDelimiter Then
  326.             If (idx1 = lDelimList) Then
  327.                 laDelim(lDelimList) = iChr
  328.                 lDelimList = lDelimList + 1
  329.             End If
  330.             If (idx2 < lNotDelim) Then
  331.                 lNotDelim = lNotDelim - 1
  332.                 laNotDel(idx2) = laNotDel(lNotDelim)
  333.             End If
  334.         Else
  335.             If (idx2 = lNotDelim) Then
  336.                 laNotDel(lNotDelim) = iChr
  337.                 lNotDelim = lNotDelim + 1
  338.             End If
  339.             If (idx1 < lDelimList) Then
  340.                 lDelimList = lDelimList - 1
  341.                 laDelim(idx1) = laDelim(lDelimList)
  342.             End If
  343.         End If
  344.     Next iPos
  345. ErrHandler:
  346. End Sub
  347.  
  348. '  Rd - crYptic but cRaZy                                      :)
  349. ' ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ
  350. '
  351.