• Welcome to PlanetSquires Forums.
 

AfxStr.inc idea

Started by Paul Squires, December 30, 2017, 08:43:56 AM

Previous topic - Next topic

Paul Squires

I haven't written the code but it does seem like it could be trivial. I suggest a function that clips a substring from a string based on two matching characters. Many times I need to extract text that appears between two different types of matching characters (for example, text between an open parenthesis and a close parenthesis).

I could us INSTR to get the first character and then AfxStrExtract to get the string up to the next character but it would be cool if it was all in one function call.

i = INSTR(wszMainStr, "(")
If i Then wszSubString = AfxStrExtract ( i+ 1, wszMainStr, ")" )

Maybe something like OVERLOAD the current AfxStrExtract function so that the first parameter is a start character to search for rather than a start position?

PRIVATE FUNCTION AfxStrExtract OVERLOAD (BYREF wszStartMatchStr AS WSTRING, BYREF wszMainStr AS WSTRING, BYREF wszMatchStr AS WSTRING) AS CWSTR

I need to express my appreciation for all of the AfxStr functions. Since starting to use them in my code it has made FB programming incredibly easy to the point where it rivals and surpasses string handling in PowerBasic. If you guys are not using Jose's WinFBX library than you are missing out on so much.


Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#1
This can be done using regular expressions, e.g.


'#CONSOLE ON
#define UNICODE
#INCLUDE ONCE "Afx/CRegExp.inc"
USING Afx

DIM pRegExp AS CRegExp

' // extract the first match
DIM cwsText AS CWSTR = "blah blah (text beween parentheses) blah blah"
DIM cws AS CWSTR = pRegExp.Extract(cwsText, "([^(]*?)(?=\))")
PRINT cws

' // extract the first match after the 11th position
cwsText = "blah (xxx) blah (text beween parentheses) blah blah"
cws = pRegExp.Extract(11, cwsText, "([^(]*?)(?=\))")
PRINT cws

PRINT
PRINT "Press any key..."
SLEEP


In the pattern "([^(]*?)(?=\))" the characters in bold are the starting and ending delimiters. Therefore, to match other delimiters you only need to change them, e.g. "([^{]*?)(?=\})" (extracts text between curly braces).

If you want to include the delimiters in the extracted text, you can use this pattern: "\(.*?\)")

José Roca

> I need to express my appreciation for all of the AfxStr functions.

Well, all started with the ansi functions that you posted time ago, so you're the co-author :)

Paul Squires

Quote from: Jose Roca on December 30, 2017, 10:46:52 AM
> I need to express my appreciation for all of the AfxStr functions.

Well, all started with the ansi functions that you posted time ago, so you're the co-author :)

Lol, my contribution pales in comparison to the work you've done. :) 
Here's to making 2018 a good Freebasic year with lots of tools and productivity.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#4
These two overloaded functions seems to work fine, allow an starting position and the size of the delimiters aren't limited to 1 character.


' ========================================================================================
' 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: 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 cwsText AS CWSTR = "blah blah (text beween parentheses) blah blah"
' print AfxStrExtract(cwsText, "(", ")")
' ========================================================================================
PRIVATE FUNCTION AfxStrExtract OVERLOAD (BYREF wszMainStr AS WSTRING, BYREF wszDelim1 AS WSTRING, BYREF wszDelim2 AS WSTRING) AS CWSTR
   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 AfxStrExtract OVERLOAD (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS WSTRING, BYREF wszDelim1 AS WSTRING, BYREF wszDelim2 AS WSTRING) AS CWSTR
   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
' ========================================================================================


Paul Squires

Thanks Jose! I've been using it over the past hour and it seems to work quite well in my code so far. Awesome!  :)
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

Paul Squires

Jose, I've updated to use your very latest WinFBX library code from GitHub. I was able to recompile WinFBE with no errors.
You may want to update your Hstory.txt file to reflect the AfxStrextract changes.
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca


Paul Squires

Hi Jose, as I am coding today I came across another idea that would be a great time saver for certain situations and it is a compliment to the new AfxStrExtract functions. Maybe extend the AfxStrRemove functions to remove portions of string between matching characters.

For example, in my code I have a need to remove the parenthesis enclosing array variables. I can do it using INSTR and MID, etc but a dedicated function would be nice.

st = "DIM myArray(10) AS LONG"

st = AfxStrRemove( st, "(", ")" )

Result: "DIM myArray AS LONG"

What do you think?
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#9
Done.


' ========================================================================================
' 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
' This function is case-sensitive.
' Example:
' DIM cwsText AS CWSTR = "blah blah (text beween parentheses) blah blah"
' print AfxStrRemove(cwsText, "(", ")")   ' Returns "blah blah  blah blah"
' ========================================================================================
PRIVATE FUNCTION AfxStrRemove OVERLOAD (BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS WSTRING, BYREF wszDelim2 AS WSTRING) AS CWSTR
   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
   RETURN MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2)
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxStrRemove OVERLOAD (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS WSTRING, BYREF wszDelim2 AS WSTRING) AS CWSTR
   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
   RETURN MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2)
END FUNCTION
' ========================================================================================


The delimiters aren't limited to one character. Therefore, you can use " (" if you want to remove a leading space.

Paul Squires

I just tried it in my code and it works as advertised - thanks!

I am wondering about the situation where multiple instances of the pattern are present in the string. Maybe an AfxStrRemoveAll function?

dim as string st = "As Long var1(34), var2(  73 ), var3(any)"
st = AfxStrRemove(st, "(", ")")
? "Results:  "As Long var1, var2(  73 ), var3(any)"

Whereas an AfxStrRemoveAll would yield:

dim as string st = "As Long var1(34), var2(  73 ), var3(any)"
st = AfxStrRemoveAll(st, "(", ")")
? "Results:  "As Long var1, var2, var3"
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

I have added a boolean flag, fRemoveAll. If TRUE, the functions will remove all the occurrences recursively.


' ========================================================================================
' 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 cwsText AS CWSTR = "blah blah (text beween parentheses) blah blah"
' print AfxStrRemove(cwsText, "(", ")")   ' Returns "blah blah  blah blah"
' ========================================================================================
PRIVATE FUNCTION AfxStrRemove OVERLOAD (BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS WSTRING, BYREF wszDelim2 AS WSTRING, BYVAL fRemoveAll AS BOOLEAN = FALSE) AS CWSTR
   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 AfxStrRemove(MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2), wszDelim1, wszDelim2, fRemoveAll)
END FUNCTION
' ========================================================================================
' ========================================================================================
PRIVATE FUNCTION AfxStrRemove OVERLOAD (BYVAL nStart AS LONG = 1, BYREF wszMainStr AS CONST WSTRING, BYREF wszDelim1 AS WSTRING, BYREF wszDelim2 AS WSTRING, BYVAL fRemoveAll AS BOOLEAN = FALSE) AS CWSTR
   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 AfxStrRemove(nStart, MID(wszMainStr, 1, nPos1 - 1) & MID(wszMainStr, nPos2), wszDelim1, wszDelim2, fRemoveAll)
END FUNCTION
' ========================================================================================


Paul Squires

Awesome Jose, works perfectly  :)
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer