[Implemented] More Flexable Version of FindString

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
oldefoxx
Enthusiast
Enthusiast
Posts: 532
Joined: Fri Jul 25, 2003 11:24 pm

[Implemented] More Flexable Version of FindString

Post by oldefoxx »

Without breaking with current FindString() syntax, I would like to see two added capabilities: (1) the ability to have a case-insensitive search for string matches, and (2) the addition of an optional paramter to limit the search range within the parameter String$. A zero means no limit, except the length of String$. any other value means stop at that point if within the length of String$
has-been wanna-be (You may not agree with what I say, but it will make you think).
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

I haven't tested it but maybe you can use something like this while you are waiting:

Code: Select all

Macro FindStringEx(String, StringToFind, StartPos, CaseSensitive = 0, StopPos = 0)
  CompilerIf CaseSensitive
    CompilerIf StopPos > 0
      FindString(UCase(String), UCase(Left(StringToFind, StopPos)), StartPos)
    CompilerElse
      FindString(UCase(String), UCase(StringToFind), StartPos)
    CompilerEndIf
  CompilerElse
    CompilerIf StopPos > 0
      FindString(String, Left(StringToFind, StopPos), StartPos)
    CompilerElse
      FindString(String, StringToFind, StartPos)
    CompilerEndIf
  CompilerEndIf
EndMacro
oldefoxx
Enthusiast
Enthusiast
Posts: 532
Joined: Fri Jul 25, 2003 11:24 pm

Thanks, but here is the problem ...

Post by oldefoxx »

... This could be a help to others, might even convince some people that my request is not really needed. But the Problem with using Mid(), Left(), Right(). UCase(), and LCase() in this manner is that they result in temporary strings, and creating temporary strings cause a lot more processing time to be invloved,

So this is an area that can really be enhanced with optimized code, that can process strings in place, and that is why I brought it up. It is certainly possible to write equivalent functionality in ASM, PB syntax, or a combination of the two, or even with a Macro as you illustrated, but it occurred to me that it would be a worthy addition to the PB syntax. It would create one of the most adaptive FindSrting() implementations around.

Though it would still lack the reverse search sequence (searching right to left) that PowerBasic uses with its INSTR() function. It does this by using a negative value for the offset to indicate processing from the right. So a -1 would mean "Start processing with the first character of String$ from the right". But I don't thinkg that the PowerBasic use recognizes that if you are searching for a five character StringToFind$, that you don't want to actually start with -1 when searching from the right, you want to start with -Len(FindToString$), which is where the first possible match could possibly occur?

So the BP implementation could be made that much smarter and faster -- maybe. Only problem is that after your first match, say when right-to-left searching "AAA" in a series of "AAAAAAAAAAAAAAAAA", should the second match be at -4, or -6? The right answer is that going in reverse in this manner should work exactly the opposite as going in the present directon, so that the transposition is merely in the use of a sign flag for the offset. But the underlying seach mechanism could be just a tad smarter to make sure we don't end up performing useless cycles.
has-been wanna-be (You may not agree with what I say, but it will make you think).
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

Trond wrote:I haven't tested it but maybe you can use something like this while you are waiting:

Code: Select all

Macro FindStringEx(String, StringToFind, StartPos, CaseSensitive = 0, StopPos = 0)
...
@Trond

do you know what happens, if CaseSensitive and StopPos are variables? At compile time the compiler does not know, which value the variables will have! But I think it is different to the default value "0".

If the compiler handles variables different to "0" then the result of CaseSensitive=variable and StopPos=variable will be:

Code: Select all

FindString(UCase(String), UCase(Left(StringToFind, StopPos)), StartPos)
I didn't make the test.

What do you think?

cu, helpy
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

No idea at all. :roll:
oldefoxx
Enthusiast
Enthusiast
Posts: 532
Joined: Fri Jul 25, 2003 11:24 pm

Wait! A whole new concept!

Post by oldefoxx »

PowerBasic uses a keyword ANY with some string commands, so if I say
a = INSTR(String$, ANY "1234567890"), I mean search until you find any one of those characters in String$.

Not that I am trying to steal PowerBasic's thunder, but I think ANY could be used much more effectively. Instead of just a compiler directive, what if ANY became a directive and a function in its own right? So that if I say Print( Any(2, "ALAKASAZARCACOCTDEDCFMFLGAGUHIIDILINIAKSKYLAMEMHMDMAMIMNMSMOMTNENHNJNMNYNCNDMPOHOKPWPAPRRISCSDTNTXUTVTVIVAWAWVWIWY"))
It would mean to repeat a process, returning two characters each time, until it finally returnes null characters (""). In the example above, I have most of the postal codes used by the US Post Office.

Or I could say Offset = FinstString(Text$, ANY(3, "JANFEBMARAPRMAYJUNJULAUGOCTNOVDEC"), 1) and try to search for a matching month. Or I could say Offset = FindString( Text$, ANY(3, "SunMonTueWedThuFriSat"), 1) to try and find a matching day. Or I could be more restricting and say Offset=FindString(Text$," " + ANY(3, "SunMonTueWedThuFriSat"), 1), or a=FindString(Text$, Any(4, " Sun Mon Tue Wed Thu Fri Sat Sun"), 1), or even get really creative and say:
DateDay=FindString(Text$, Any " " + ANY(3, "JanFebMarAprMayJunJulAugOctNovDecSunMonTueWedThuFriSat"), 1) to see if I can find any text where it appears that a date or day may be specified.

Not to say that ANY() would be used by itself for text matches. Rather, it would work in conjunction with FindString, CountString, and StringField, Print, and other commands to repeat a given process that incrementally works through the string specified by ANY in increments that the user specifies, such as a=CountString(String$, index, Any(1, " ;:,.<>/?!@#$%^&*()-_=+[]{}"+Chr(13)+Chr(10)+Chr(9))
has-been wanna-be (You may not agree with what I say, but it will make you think).
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

I made the following test:

Code: Select all

Macro FindStringEx(String, StringToFind, StartPos, CaseSensitive = 0, StopPos = 0) 
  CompilerIf CaseSensitive 
    CompilerIf StopPos > 0 
      FindString(UCase(String), UCase(Left(StringToFind, StopPos)), StartPos) 
    CompilerElse 
      FindString(UCase(String), UCase(StringToFind), StartPos) 
    CompilerEndIf 
  CompilerElse 
    CompilerIf StopPos > 0 
      FindString(String, Left(StringToFind, StopPos), StartPos) 
    CompilerElse 
      FindString(String, StringToFind, StartPos) 
    CompilerEndIf 
  CompilerEndIf 
EndMacro

StartPos = 0
CaseSensitive = 0
StopPos = 5

test = FindStringEx("abcdef","c",StartPos,CaseSensitive,StopPos)
Debug test

CaseSensitive = 1
test = FindStringEx("abcdef","c",StartPos,CaseSensitive,StopPos)
Debug test
It will not work ... because the line:

Code: Select all

test = FindStringEx("abcdef","c",StartPos,CaseSensitive,StopPos)
will be replaced by

Code: Select all

test = CompilerIf CaseSensitive 
  CompilerIf StopPos > 0 
    FindString(UCase("abcdef"), UCase(Left("c", StopPos)), StartPos) 
  CompilerElse 
    FindString(UCase("abcdef"), UCase("c"), StartPos) 
  CompilerEndIf 
CompilerElse 
  CompilerIf StopPos > 0 
    FindString("abcdef", Left("c", StopPos), StartPos) 
  CompilerElse 
    FindString("abcdef", "c", StartPos) 
  CompilerEndIf 
CompilerEndIf 
And this results in the following error:
Error: Line 21 - A variable can't be named the same as a PureBasic keyword: CompilerIf
at line 1 of the expanded macro (Macro.out)
cu, helpy
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

Changed the test to:

Code: Select all

Macro FindStringEx(Result,String, StringToFind, StartPos, CaseSensitive = 0, StopPos = 0) 
  CompilerIf CaseSensitive 
    CompilerIf StopPos > 0 
      Result = FindString(UCase(String), UCase(Left(StringToFind, StopPos)), StartPos) 
    CompilerElse 
      Result = FindString(UCase(String), UCase(StringToFind), StartPos) 
    CompilerEndIf 
  CompilerElse 
    CompilerIf StopPos > 0 
      Result = FindString(String, Left(StringToFind, StopPos), StartPos) 
    CompilerElse 
      Result = FindString(String, StringToFind, StartPos) 
    CompilerEndIf 
  CompilerEndIf 
EndMacro

StartPos = 0
CaseSensitive = 0
StopPos = 5

FindStringEx(test,"abcdef","c",StartPos,CaseSensitive,StopPos)
Debug test

CaseSensitive = 1
test = FindStringEx(test,"abcdef","c",StartPos,CaseSensitive,StopPos)
Debug test
This results in the following error:

Code: Select all

Error: Line 21 - Integer constant expression expected for compiler directives
at line 2 of the expanded macro (Macro.out)
That means, that if macro parameters are used in CompilerIf expressions, they have to be numerical constants. It is not allowed to pass variables to CompilerIf statement.

This indeed is logical !!

cu, helpy
Lebostein
Addict
Addict
Posts: 826
Joined: Fri Jun 11, 2004 7:07 am

Post by Lebostein »

helpy wrote:This indeed is logical !!
Look here for more informations about the "CompilerIf and macro"-problem:
http://www.purebasic.fr/english/viewtop ... 243#134243

Macros allowed inside CompilerIF constructions only. Look at this example:

Problem: CompilerIf inside macro

Code: Select all

Macro FunctionCall
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    CallFunction
  CompilerElse
    CallCFunction
  CompilerEndIf
EndMacro

FunctionCall(1, "test")
Solution: macro inside CompilerIf

Code: Select all

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Macro FunctionCall
    CallFunction
  EndMacro
CompilerElse
  Macro FunctionCall
    CallCFunction
  EndMacro
CompilerEndIf

FunctionCall(1, "test")
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

Lebostein wrote:Macros allowed inside CompilerIF constructions only.
Does that mean, that CompilerIf is not allowed inside macros? That would be bad, if future compiler versions would reject the use of CompilerIf inside macros!

I use CompilerIf statements inside macros to check the previous use of other macros. For instance:

Code: Select all

Macro DQ
  "
EndMacro

Macro _MakeString(s)
  DQ#s#DQ
EndMacro

Macro TEST_START(Name)
  CompilerIf Defined(__TEST__#Name#__TEST__,#PB_Constant)
    CompilerError "TEST1: <" + _MakeString(Name) + "> is already in use!"
  CompilerElse
    #__TEST__#Name#__TEST__ = #True
  CompilerEndIf
  
  ; some other things doing with "Name"
EndMacro

Macro TEST_END(Name)
  CompilerIf Defined(__TEST__#Name#__TEST__,#PB_Constant) = 0
    CompilerError "TEST2: <" + _MakeString(Name) + "> was not used with TEST1!"
  CompilerEndIf
  
  ; some other things doing with "Name"
EndMacro

TEST_START(test1)
; doing other stuff in the context of "test1)
TEST_END(test1)

;TEST_START(test1) ; will cause a compile error
;TEST_END(test2)   ; will cause a compile error
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

CompilerIf works inside macros without problem. It just does not work with a variable, which makes sense.
quidquid Latine dictum sit altum videtur
Post Reply