So far, the only bug that I have found was that the line m_pBuffer[m_BufferLen] = 0 in the ResizeBuffer method was misplaced. I have updated the code in the original post.
String functions. They work with STRING, ZSTRING, WSTRING, DWSTRING and string literals.
Code: Select all
' ########################################################################################
' Platform: Microsoft Windows
' Filename: DWStrProcs.bi
' Contents: String wrapper functions.
' Compiler: FreeBasic 32 & 64-bit, Unicode.
' Copyright (c) 2018 José Roca. Freeware. Use at your own risk.
' ########################################################################################
#pragma once
#include once "DWString.bi"
' ========================================================================================
' * Returns a copy of a string with substrings removed.
' If wszMatchStr is not present in wszMainStr, all of wszMainStr is returned intact.
' This function is case sensitive.
' Example: DWStrRemove("Hello World. Welcome to the Freebasic World", "World")
' ========================================================================================
PRIVATE FUNCTION DWStrRemove OVERLOAD (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dws AS DWSTRING = wszMainStr
DIM nLen AS LONG = LEN(wszMatchStr)
DO
DIM nPos AS LONG = INSTR(**dws, wszMatchStr)
IF nPos = 0 THEN EXIT DO
dws.DelChars nPos, nLen
LOOP
RETURN dws
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrRemove.
' Example: DWStrRemoveI("Hello World. Welcome to the Freebasic World", "world")
' ========================================================================================
PRIVATE FUNCTION DWStrRemoveI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM dwsMatchStr AS DWSTRING = UCASE(wszMatchStr)
DIM nLen AS LONG = LEN(wszMatchStr)
DO
DIM nPos AS LONG = INSTR(UCASE(**dwsMainStr), **dwsMatchStr)
IF nPos = 0 THEN EXIT DO
dwsMainStr.DelChars nPos, nLen
LOOP
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Returns a copy of a string with a substring enclosed between the specified delimiters removed.
' Parameters:
' nStart: [Optional]. The one-based starting position where to start the search
' wszMainStr: The main string
' wszDelim1: The first delimiter
' wszDelim2: The second delimiter
' fRemoveAll: TRUE or FALSE. TRUE = Recursively remove all the occurrences.
' This function is case-sensitive.
' Example:
' DIM dwsText AS DWSTRING = "blah blah (text beween parentheses) blah blah"
' print DWStrRemove(dwsText, "(", ")") ' Returns "blah blah blah blah"
' Example:
' DIM dwsText AS DWSTRING = "As Long var1(34), var2( 73 ), var3(any)"
' print DWStrRemove(dwsText, "(", ")", TRUE) ' Returns "As Long var1, var2, var3"
' ========================================================================================
PRIVATE FUNCTION DWStrRemove OVERLOAD (BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS CONST WSTRING, BYREF wszDelim2 AS CONST WSTRING, BYVAL fRemoveAll AS BOOLEAN = FALSE) AS DWSTRING
DIM nPos1 AS LONG = INSTR(wszMainStr, wszDelim1)
IF nPos1 = 0 THEN RETURN wszMainStr
DIM nPos2 AS LONG = INSTR(nPos1 + LEN(wszDelim1), wszMainStr, wszDelim2)
IF nPos2 = 0 THEN RETURN wszMainStr
nPos2 += LEN(wszDelim2)
DIM nLen AS LONG = nPos2 - nPos1
IF fRemoveAll = FALSE THEN RETURN MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2)
RETURN DWStrRemove(MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2), wszDelim1, wszDelim2, fRemoveAll)
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION DWStrRemove OVERLOAD (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS CONST WSTRING, BYREF wszDelim2 AS CONST WSTRING, BYVAL fRemoveAll AS BOOLEAN = FALSE) AS DWSTRING
DIM nLen AS LONG = LEN(wszMainStr)
IF (nStart = 0) OR (nStart > nLen) THEN RETURN ""
IF nStart < 0 THEN nStart = nLen + nStart + 1
DIM nPos1 AS LONG = INSTR(nStart, wszMainStr, wszDelim1)
IF nPos1 = 0 THEN RETURN wszMainStr
DIM nPos2 AS LONG = INSTR(nPos1, wszMainStr, wszDelim2)
IF nPos2 = 0 THEN RETURN wszMainStr
nPos2 += LEN(wszDelim2)
nLen = nPos2 - nPos1
IF fRemoveAll = FALSE THEN RETURN MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2)
RETURN DWStrRemove(nStart, MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2), wszDelim1, wszDelim2, fRemoveAll)
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Returns a copy of a string with characters removed.
' If wszMatchStr is not present in wszMainStr, all of wszMainStr is returned intact.
' wszMatchStr specifies a list of single characters to be searched for individually,
' a match on any one of which will cause that character to be removed from the result.
' This function is case sensitive.
' Example: DWStrRemoveAny("abacadabra", "bac") ' -> "dr"
' ========================================================================================
PRIVATE FUNCTION DWStrRemoveAny (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
FOR i AS LONG = 1 TO LEN(wszMatchStr)
DO
DIM nPos AS LONG = INSTR(**dwsMainStr, MID(wszMatchStr, i, 1))
IF nPos = 0 THEN EXIT DO
dwsMainStr.DelChars nPos, 1
LOOP
NEXT
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrRemoveAny.
' Example: DWStrRemoveAnyI("abacadabra", "BaC") ' -> "dr"
' ========================================================================================
PRIVATE FUNCTION DWStrRemoveAnyI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM dwsMatchStr AS DWSTRING = UCASE(wszMatchStr)
FOR i AS LONG = 1 TO LEN(wszMatchStr)
DO
DIM nPos AS LONG = INSTR(UCASE(**dwsMainStr), MID(**dwsMatchStr, i, 1))
IF nPos = 0 THEN EXIT DO
dwsMainStr.DelChars nPos, 1
LOOP
NEXT
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Within a specified string, replace all occurrences of one string with another string.
' Replaces all occurrences of wszMatchStr in wszMainStr with wszReplaceWith
' The replacement can cause wszMainStr to grow or condense in size.
' When a match is found, the scan for the next match begins at the position immediately
' following the prior match.
' This function is case sensitive.
' Example: DWStrReplace("Hello World", "World", "Earth") ' -> "Hello Earth"
' ========================================================================================
PRIVATE FUNCTION DWStrReplace OVERLOAD (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYREF wszReplaceWith AS WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM nLenReplaceWith AS LONG = LEN(wszReplaceWith)
DIM nLenMatchStr AS LONG = LEN(wszMatchStr)
DIM nPos AS LONG = 1
DO
nPos = INSTR(nPos, **dwsMainStr, wszMatchStr)
IF nPos = 0 THEN EXIT DO
dwsMainStr = MID(**dwsMainStr, 1, nPos - 1) + wszReplaceWith + MID(**dwsMainStr, nPos + nLenMatchStr)
nPos += nLenReplaceWith
LOOP
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrReplace.
' Example: DwStrReplaceI("Hello world", "World", "Earth") ' -> "Hello Earth"
' ========================================================================================
PRIVATE FUNCTION DWStrReplaceI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYREF wszReplaceWith AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM dwsMatchStr AS DWSTRING = UCASE(wszMatchStr)
DIM nLenReplaceWith AS LONG = LEN(wszReplaceWith)
DIM nLenMatchStr AS LONG = LEN(wszMatchStr)
DIM nPos AS LONG = 1
DO
nPos = INSTR(nPos, UCASE(**dwsMainStr), **dwsMatchStr)
IF nPos = 0 THEN EXIT DO
dwsMainStr = MID(**dwsMainStr, 1, nPos - 1) + wszReplaceWith + MID(**dwsMainStr, nPos + nLenMatchStr)
nPos += nLenReplaceWith
LOOP
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Within a specified string, replace all occurrences of any of the individual characters
' specified in the wszMainStr string.
' wszReplaceWith must be a single character. This function does not replace words therefore
' wszMatchStr will be the same size - it will not shrink or grow.
' This function is case-sensitive.
' Example: DWStrReplaceAny("abacadabra", "bac", "*") ' -> *****d**r*
' ========================================================================================
PRIVATE FUNCTION DWStrReplaceAny (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYREF wszReplaceWith AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
IF LEN(wszMatchStr) = 0 THEN RETURN dwsMainStr
IF LEN(wszReplaceWith) = 0 THEN RETURN dwsMainStr
FOR x AS LONG = 1 TO LEN(wszMatchStr)
FOR i AS LONG = 1 TO LEN(wszMainStr)
IF MID(wszMatchStr, x, 1) = MID(wszMainStr, i, 1) THEN
MID(**dwsMainStr, i, 1) = wszReplaceWith
END IF
NEXT
NEXT
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrReplaceAny.
' Example: DWStrReplaceAnyI("abacadabra", "BaC", "*") ' -> *****d**r*
' ========================================================================================
PRIVATE FUNCTION DWStrReplaceAnyI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYREF wszReplaceWith AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
IF LEN(wszMatchStr) = 0 THEN RETURN dwsMainStr
IF LEN(wszReplaceWith) = 0 THEN RETURN dwsMainStr
FOR x AS LONG = 1 TO LEN(wszMatchStr)
FOR i AS LONG = 1 TO LEN(wszMainStr)
IF MID(UCASE(wszMatchStr), x, 1) = MID(UCASE(wszMainStr), i, 1) THEN
MID(**dwsMainStr, i, 1) = wszReplaceWith
END IF
NEXT
NEXT
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Reverses the contents of a string expression.
' Usage example: DIM dws AS DWSTRING = DWStrReverse("garden")
' ========================================================================================
PRIVATE FUNCTION DWStrReverse (BYREF wszMainStr AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM wszChar AS WSTRING * 2
DIM nLen AS LONG = LEN(wszMainStr)
FOR i AS LONG = 1 TO nLen \ 2
wszChar = MID(**dwsMainStr, i, 1)
MID(**dwsMainStr, i, 1) = MID(**dwsMainStr, nLen - i + 1, 1)
MID(**dwsMainStr, nLen - i + 1, 1) = wszChar
NEXT
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Extracts characters from a string up to a character or group of characters.
' Complement function to DWStrRemain.
' Returns a substring of wszMainStr starting with its first character (or the character
' specified by nStart) and up to (but not including) the first occurrence of wszMatchStr
' If wszMatchStr is not present in wszMainStr (or is null) then all of wszMainStr is
' returned from the nStart position.
' This function is case-sensitive.
' The following line returns "aba" (match on "cad")
' DIM dws AS DWSTRING = DWStrExtract(1, "abacadabra","cad")
' ========================================================================================
PRIVATE FUNCTION DWStrExtract OVERLOAD (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM nLen AS LONG = LEN(wszMainStr)
IF (nStart = 0) OR (nStart > nLen) THEN RETURN ""
IF nStart < 0 THEN nStart = nLen + nStart + 1
DIM nPos AS LONG = INSTR(nStart, wszMainStr, wszMatchStr)
IF nPos THEN RETURN MID(wszMainStr, nStart, nPos - nStart)
RETURN MID(wszMainStr, nStart)
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrExtract.
' DIM dws AS DWSTRING = DWStrExtractI(1, "abacadabra","CaD")
' ========================================================================================
PRIVATE FUNCTION DWStrExtractI (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dws AS DWSTRING = wszMainStr
DIM nLen AS LONG = LEN(wszMainStr)
IF (nStart = 0) OR (nStart > nLen) THEN RETURN ""
IF nStart < 0 THEN nStart = nLen + nStart + 1
DIM nPos AS LONG = INSTR(nStart, UCASE(wszMainStr), UCASE(wszMatchStr))
IF nPos THEN RETURN MID(wszMainStr, nStart, nPos - nStart )
RETURN dws = MID(wszMainStr, nStart)
END FUNCTION
' ========================================================================================
' ========================================================================================
' Returns the portion of a string following the occurrence of a specified delimiter up to
' the second delimiter. If one of the delimiters isn't found, it returns an empty string.
' Parameters:
' nStart: [Optional]. The one-based starting position where to start the search
' wszMainStr: The main string
' wszDelim1: The first delimiter
' wszDelim2: The second delimiter
' This function is case-sensitive.
' Example:
' DIM dwsText AS DWSTRING = "blah blah (text beween parentheses) blah blah"
' print DWStrExtract(dwsText, "(", ")")
' ========================================================================================
PRIVATE FUNCTION DWStrExtract OVERLOAD (BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS CONST WSTRING, BYREF wszDelim2 AS CONST WSTRING) AS DWSTRING
DIM nPos1 AS LONG = INSTR(wszMainStr, wszDelim1)
IF nPos1 = 0 THEN RETURN ""
nPos1 += LEN(wszDelim1)
DIM nPos2 AS LONG = INSTR(nPos1, wszMainStr, wszDelim2)
IF nPos2 = 0 THEN RETURN ""
DIM nLen AS LONG = nPos2 - nPos1
RETURN MID(wszMainStr, nPos1, nLen)
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION DWStrExtract OVERLOAD (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS CONST WSTRING, BYREF wszDelim2 AS CONST WSTRING) AS DWSTRING
DIM nLen AS LONG = LEN(wszMainStr)
IF (nStart = 0) OR (nStart > nLen) THEN RETURN ""
IF nStart < 0 THEN nStart = nLen + nStart + 1
DIM nPos1 AS LONG = INSTR(nStart, wszMainStr, wszDelim1)
IF nPos1 = 0 THEN RETURN ""
nPos1 += LEN(wszDelim1)
DIM nPos2 AS LONG = INSTR(nPos1, wszMainStr, wszDelim2)
IF nPos2 = 0 THEN RETURN ""
nLen = nPos2 - nPos1
RETURN MID(wszMainStr, nPos1, nLen)
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Extract characters from a string up to a specific character.
' Returns a substring of wszMainStr starting with its first character (or the character
' specified by nStart) and up to (but not including) the first occurrence of wszMatchStr.
' wszMatchStr specifies a list of single characters to be searched for individually, a
' match on any one of which will cause the extract operation to be performed up to that character.
' If wszMatchStr is not present in wszMainStr (or is null) then all of wszMainStr is returned.
' This function is case-sensitive.
' The following line returns "aba" (match on "c")
' Example: DWStrExtractAny(1, "abacadabra","cd")
' ========================================================================================
PRIVATE FUNCTION DWStrExtractAny (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM nLen AS LONG = LEN(wszMainStr)
IF (nStart = 0) OR (nStart > nLen) THEN RETURN ""
IF nStart < 0 THEN nStart = nLen + nStart + 1
FOR i AS LONG = nStart TO nLen
FOR x AS LONG = 1 TO LEN(wszMatchStr)
IF MID(wszMainStr, i, 1) = MID(wszMatchStr, x, 1) THEN
dwsMainStr = MID(wszMainStr, nStart, i - nStart)
RETURN dwsMainStr
END IF
NEXT
NEXT
RETURN ""
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrExtractAny.
' Example: DWStrExtractAnyI(1, "abacadabra","CD")
' ========================================================================================
PRIVATE FUNCTION DWStrExtractAnyI (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS DWSTRING
DIM dwsMainStr AS DWSTRING = wszMainStr
DIM nLen AS LONG = LEN(wszMainStr)
IF (nStart = 0) OR (nStart > nLen) THEN RETURN ""
IF nStart < 0 THEN nStart = nLen + nStart + 1
FOR i AS LONG = nStart TO nLen
FOR x AS LONG = 1 TO LEN(wszMatchStr)
IF MID(UCASE(wszMainStr), i, 1) = MID(UCASE(wszMatchStr), x, 1) THEN
dwsMainStr = MID(wszMainStr, nStart, i - nStart)
RETURN dwsMainStr
END IF
NEXT
NEXT
RETURN ""
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Complement to the DWStrExtract function.
' Returns the portion of a string following the first occurrence of a substring.
' wszMainStr is searched for the string specified in wszMatchStr If found, all characters
' after wszMatchStr are returned. If wszMatchStr is not present in wszMainStr (or is null) then
' a zero-length empty string is returned.
' nStart is an optional starting position to begin searching. If nStart is not specified,
' position 1 will be used. If nStart is zero, a nul string is returned. If nStart is negative,
' the starting position is counted from right to left: if -1, the search begins at the last
' character; if -2, the second to last, and so forth.
' This function is case-sensitive.
' Example: DWStrRemain("Brevity is the soul of wit", "is ") ' -> "the soul of wit"
' ========================================================================================
PRIVATE FUNCTION DWStrRemain (BYREF wszMainStr AS WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYVAL nStart AS LONG = 1) AS DWSTRING
IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN RETURN ""
IF nStart = 0 OR nStart > LEN(wszMainStr) THEN RETURN ""
IF nStart < 0 THEN nStart = LEN(wszMainStr) + nStart + 1
DIM nPos AS LONG = INSTR(nStart, wszMainStr, wszMatchStr)
IF nPos = 0 THEN RETURN ""
DIM dwsMainStr AS DWSTRING = wszMainStr
dwsMainStr = MID(**dwsMainStr, nPos + LEN(wszMatchStr))
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrRemain.
' Example: DWStrRemainI("Brevity is the soul of wit", "Is ") ' -> "the soul of wit"
' ========================================================================================
PRIVATE FUNCTION DWStrRemainI (BYREF wszMainStr AS WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYVAL nStart AS LONG = 1) AS DWSTRING
IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN RETURN ""
IF nStart = 0 OR nStart > LEN(wszMainStr) THEN RETURN ""
IF nStart < 0 THEN nStart = LEN(wszMainStr) + nStart + 1
DIM nPos AS LONG = INSTR(nStart, UCASE(wszMainStr), UCASE(wszMatchStr))
IF nPos = 0 THEN RETURN ""
DIM dwsMainStr AS DWSTRING = wszMainStr
dwsMainStr = MID(**dwsMainStr, nPos + LEN(wszMatchStr))
RETURN dwsMainStr
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Complement to the DWStrExtract function. Returns the portion of a string following the
' first occurrence of a character or group of characters.
' wszMainStr is searched for the string specified in wszMatchStr If found, all characters
' after wszMatchStr are returned. If wszMatchStr is not present in wszMainStr (or is null) then
' a zero-length empty string is returned.
' wszMatchStr specifies a list of single characters to be searched for individually. A match
' on any one of which will cause the extract operation be performed after that character.
' nStart is an optional starting position to begin searching. If nStart is not specified,
' position 1 will be used. If nStart is zero, a nul string is returned. If nStart is negative,
' the starting position is counted from right to left: if -1, the search begins at the last
' character; if -2, the second to last, and so forth.
' This function is case-sensitive.
' Example: DWStrRemainAny("I think, therefore I am", ",") ' -> " therefore I am"
' ========================================================================================
PRIVATE FUNCTION DWStrRemainAny (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS WSTRING, BYVAL nStart AS LONG = 1) AS DWSTRING
IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN RETURN ""
IF nStart = 0 OR nStart > LEN(wszMainStr) THEN RETURN ""
IF nStart < 0 THEN nStart = LEN(wszMainStr) + nStart + 1
DIM dwsMainStr AS DWSTRING
FOR i AS LONG = nStart TO LEN(wszMainStr)
FOR x AS LONG = 1 TO LEN(wszMatchStr)
IF MID(wszMainStr, i, 1) = MID(wszMatchStr, x, 1) THEN
dwsMainStr = MID(wszMainStr, i + 1)
RETURN dwsMainStr
END IF
NEXT
NEXT
RETURN ""
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrRemainAny.
' Example: DWStrRemainAnyI("I think, therefore I am", "E") ' -> "refore I am"
' ========================================================================================
PRIVATE FUNCTION DWStrRemainAnyI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING, BYVAL nStart AS LONG = 1) AS DWSTRING
IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN RETURN ""
IF nStart = 0 OR nStart > LEN(wszMainStr) THEN RETURN ""
IF nStart < 0 THEN nStart = LEN(wszMainStr) + nStart + 1
DIM dwsMainStr AS DWSTRING
FOR i AS LONG = nStart TO LEN(wszMainStr)
FOR x AS LONG = 1 TO LEN(wszMatchStr)
IF MID(UCASE(wszMainStr), i, 1) = MID(UCASE(wszMatchStr), x, 1) THEN
dwsMainStr = MID(wszMainStr, i + 1)
RETURN dwsMainStr
END IF
NEXT
NEXT
RETURN ""
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Count the number of occurrences of strings within a string.
' wszMainStr is the string expression in which to count characters.
' wszMatchStr is the string expression to count all occurrences of.
' If cbMatchStr is not present in wszMainStr, zero is returned.
' When a match is found, the scan for the next match begins at the position immediately
' following the prior match.
' This function is case-sensitive.
' Example: DIM nCount AS LONG = DWStrTally("abacadabra", "ab") ' -> 2
' ========================================================================================
PRIVATE FUNCTION DWStrTally (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS LONG
DIM nCount AS LONG, nPos AS LONG = 1
DIM nLen AS LONG = LEN(wszMatchStr)
DO
nPos = INSTR(nPos, wszMainStr, wszMatchStr)
IF nPos = 0 THEN EXIT DO
nCount += 1
nPos += nLen
LOOP
RETURN nCount
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrTally.
' Example: DIM nCount AS LONG = DWStrTallyI("abacadabra", "Ab") ' -> 2
' ========================================================================================
PRIVATE FUNCTION DWStrTallyI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS LONG
DIM nCount AS LONG, nPos AS LONG = 1
DIM dwsMainStr AS DWSTRING = UCASE(wszMainStr)
DIM dwsMatchStr AS DWSTRING = UCASE (wszMatchStr)
DIM nLen AS LONG = LEN(wszMatchStr)
DO
nPos = INSTR(nPos, **dwsMainStr, **dwsMatchStr)
IF nPos = 0 THEN EXIT DO
nCount += 1
nPos += nLen
LOOP
RETURN nCount
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Count the number of occurrences of specified characters strings within a string.
' wszMainStr is the string expression in which to count characters.
' wszMatchStr is a list of single characters to be searched for individually. A match on
' any one of which will cause the count to be incremented for each occurrence of that
' character. Note that repeated characters in wszMatchStr will not increase the count.
' This function is case-sensitive.
' Example: DIM nCount AS LONG = DWStrTallyAny("abacadabra", "bac") ' -> 8
' ========================================================================================
PRIVATE FUNCTION DWStrTallyAny (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS LONG
IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN EXIT FUNCTION
' // Remove possible duplicates in the matches string
DIM nPos AS LONG
DIM dwsMatchStr AS DWSTRING = wszMatchStr
FOR i AS LONG = 1 TO LEN(dwsMatchStr)
nPos = INSTR(**dwsMatchStr, MID(wszMatchStr, i, 1))
IF nPos = 0 THEN dwsMatchStr += MID(wszMatchStr, i, 1)
NEXT
' // Do the count
DIM nCount AS LONG
FOR i AS LONG = 1 TO LEN(dwsMatchStr)
nPos = 1
DO
nPos = INSTR(nPos, wszMainStr, MID(**dwsMatchStr, i, 1))
IF nPos = 0 THEN EXIT DO
IF nPos THEN
nCount += 1
nPos += 1
END IF
LOOP
NEXT
RETURN nCount
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case insensitive version of DWStrTallyAny.
' Example: DIM nCount AS LONG = DWStrTallyAnyI("abacadabra", "bAc")
' ========================================================================================
PRIVATE FUNCTION DWStrTallyAnyI (BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS LONG
IF LEN(wszMainStr) = 0 OR LEN(wszMatchStr) = 0 THEN EXIT FUNCTION
' // Remove possible duplicates in the matches string
DIM nPos AS LONG
DIM dwsMainStr AS DWSTRING = UCASE(wszMainStr)
DIM dwsMatchStr AS DWSTRING = UCASE(wszMatchStr)
FOR i AS LONG = 1 TO LEN(dwsMatchStr)
nPos = INSTR(**dwsMatchStr, MID(wszMatchStr, i, 1))
IF nPos = 0 THEN dwsMatchStr += MID(wszMatchStr, i, 1)
NEXT
' // Do the count
DIM nCount AS LONG
FOR i AS LONG = 1 TO LEN(dwsMatchStr)
nPos = 1
DO
nPos = INSTR(nPos, **dwsMainStr, MID(**dwsMatchStr, i, 1))
IF nPos = 0 THEN EXIT DO
IF nPos THEN
nCount += 1
nPos += 1
END IF
LOOP
NEXT
RETURN nCount
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Determine whether each character of a string is present in another string.
' Returns zero if each character in wszMainStr is present in wszMatchStr
' If not, it returns the position of the first non-matching character in wszMainStr.
' This function is very useful for determining if a string contains only numeric digits, for example.
' This function is case-sensitive.
' If nStart evaluates to a position outside of the string, or if nStart is zero, then the
' function returns zero.
' Example: DIM nCount AS LONG = DWStrVerify(5, "123.65,22.5", "0123456789") ' -> 7
' Rreturns 7 since 5 starts it past the first non-digit ("." at position 4)
' ========================================================================================
PRIVATE FUNCTION DWStrVerify (BYVAL nStart AS LONG, BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS LONG
IF nStart <= 0 OR nStart > LEN(wszMainStr) THEN RETURN 0
' // Get each character in wszMainStr and look for it in wszMatchStr
DIM AS LONG nPos, idx
FOR i AS LONG = nStart TO LEN(wszMainStr)
nPos = INSTR(wszMatchStr, MID(wszMainStr, i, 1))
IF nPos = 0 THEN
idx = i
EXIT FOR
END IF
NEXT
RETURN idx
END FUNCTION
' ========================================================================================
' ========================================================================================
' * Case sensintive version of DWStrVerify.
' Example: DWStrVerifyI(5, "123.65abcx22.5", "0123456789ABC") ' -> 10
' ========================================================================================
PRIVATE FUNCTION DWStrVerifyI (BYVAL nStart AS LONG, BYREF wszMainStr AS CONST WSTRING, BYREF wszMatchStr AS CONST WSTRING) AS LONG
IF nStart <= 0 OR nStart > LEN(wszMainStr) THEN RETURN 0
' // Get each character in wszMainStr and look for it in wszMatchStr
DIM dwsMainStr AS DWSTRING = UCASE(wszMainStr)
DIM dwsMatchStr AS DWSTRING = UCASE(wszMatchStr)
DIM AS LONG nPos, idx
FOR i AS LONG = nStart TO LEN(dwsMainStr)
nPos = INSTR(**dwsMatchStr, MID(**dwsMainStr, i, 1))
IF nPos = 0 THEN
idx = i
EXIT FOR
END IF
NEXT
RETURN idx
END FUNCTION
' ========================================================================================
10 Jul 2018: Changed some AfxStr... to DWStr... in the comments.