Code archives/Miscellaneous/String comparison to pattern using wildcards such as ? and *
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
Supports these operators in a pattern: ? - Matches one of any character * - Matches zero or more of any character (greedy) . - Matches zero or more of any character (lazy) + - Matches one or more of any character (greedy) % - Matches one or more of any character (lazy) Special characters can be escaped (for sake of exact character match) by preceding the character with a backslash ("\"). A literal backslash must also be escaped ("\\"). An example demonstrating the difference between greedy and lazy quantifiers: The pattern "foo*bar" will match the string "foo bar bar". The pattern "foo.bar" will not. | |||||
' --+-----------------------------------------------------------------------------------------+-- ' | This code was originally written by Sophie Kirschner (sophiek@pineapplemachine.com) | ' | It is released as public domain. Please don't interpret that as liberty to claim credit | ' | that isn't yours, or to sell this code when it could otherwise be obtained for free | ' | because that would be a really shitty thing of you to do. | ' --+-----------------------------------------------------------------------------------------+-- SuperStrict ' Example code Rem Function metatest( pattern:String, str:String, expected:Int, caseSensitive:Int = True ) Local result:Int = matchWild( pattern, str, caseSensitive ) Local failtext:String = "" If result <> expected failtext = " (FAILED!)" Print "pattern: ~q"+pattern+"~q string: ~q"+str+"~q result: "+result+" expected: "+expected+failtext End Function Print;Print "Ordinary strings" metatest( "test", "test", 1 ) metatest( "test", "toast", 0 ) Print;Print "Wildcard ?" metatest( "t?st", "test", 1 ) metatest( "t?st", "toot", 0 ) Print;Print "Wildcard *" metatest( "*", "teeeeeeEEEESSSt", 1 ) metatest( "te*st", "test", 1 ) metatest( "t*st", "teeest", 1 ) metatest( "t*st", "test test test", 1 ) metatest( "one*two*three*four", "onebbbdddtwobbthreefour", 1 ) metatest( "*yes", "testyes", 1 ) metatest( "test*", "testyes", 1 ) metatest( "*testyes*", "testyes", 1 ) metatest( "t*st", "testno", 0 ) Print;Print "Wildcard ." metatest( ".", "teeeeeeEEEESSSt", 1 ) metatest( "te.st", "test", 1 ) metatest( "t.st", "teeest", 1 ) metatest( "t.st", "test test test", 0 ) metatest( "t.st", "testno", 0 ) Print;Print "Wildcard +" metatest( "+", "teeeeeeEEEESSSt", 1 ) metatest( "te+st", "test", 0 ) metatest( "t+t", "test", 1 ) metatest( "te+st", "test test test", 1 ) metatest( "t+st", "testno", 0 ) metatest( "testno+", "testno", 0 ) metatest( "test+", "testyes", 1 ) Print;Print "Wildcard %" metatest( "%", "teeeeeeEEEESSSt", 1 ) metatest( "te%st", "test", 0 ) metatest( "t%t", "test", 1 ) metatest( "te%st", "test test test", 0 ) metatest( "testno%", "testno", 0 ) metatest( "test%", "testyes", 1 ) Print;Print "Blank strings" metatest( "*", "", 1 ) metatest( ".", "", 1 ) metatest( "+", "", 0 ) metatest( "%", "", 0 ) Print;Print "Case sensitivity" metatest( "test", "TEST", 0 ) metatest( "test", "TEST", 1, False ) Print;Print "Escaped chars" metatest( "escape\?\?", "escape??", 1 ) metatest( "a*\*", "a*", 1 ) metatest( "esc*\?\?", "escape??", 1 ) metatest( "esc*b\?\?", "escape??", 0 ) EndRem Const wildcardAscEsc:Int = Asc( "\" ) Const wildcardAsc1:Int = Asc( "?" ) Const wildcardAscPgreedy:Int = Asc( "+" ) Const wildcardAscNgreedy:Int = Asc( "*" ) Const wildcardAscPlazy:Int = Asc( "%" ) Const wildcardAscNlazy:Int = Asc( "." ) Rem Fairly simple checking whether a string conforms to a pattern. The following are various wildcards. Anything else in the pattern must be an exact match to the string. In the pattern, escape a special character by preceding it with a \. Escape a \ by writing \\. ? Match any one character + Greedily match one or more of any character. * Greedily match zero or more of any character. % Lazily match one or more of any character. . Lazily match zero or more of any character. EndRem Function matchWild:Int( pattern:String, str:String, caseSensitive:Int = True, initx:Int = 0, inity:Int = 0 ) ' Match a single character Function matchChar:Int( pattern:String, y:Int, str:String, x:Int ) 'print "[matching: "+disp(pattern,y) + " , " + disp(str,x)+"]" ' Debug If pattern[y] = wildcardAscEsc And y+1 < pattern.length If pattern[y+1] = str[x] Return 2 Else Return 0 EndIf ElseIf (pattern[y] = str[x] Or pattern[y] = wildcardAsc1) And .. Not(pattern[y] = wildcardAscPgreedy Or pattern[y] = wildcardAscNgreedy .. Or pattern[y] = wildcardAscPlazy Or pattern[y] = wildcardAscNlazy) Return 1 EndIf End Function ' Good for debugging Function disp:String( str:String, p:Int ) Return str[..p] + "[" + Chr(str[p]) + "]" + str[p+1..] End Function ' Essential vars Local x:Int = initx, y:Int = inity, ch:Int 'print "[call: "+disp(pattern,y) + " , " + disp(str,x)+"]" ' Debug ' Handle case insensitivity If Not caseSensitive str = str.ToLower() pattern = pattern.ToLower() EndIf ' Loop Repeat ' Check for termination of string and/or pattern If y >= pattern.length Return x >= str.length ElseIf x >= str.length Return y+1 >= pattern.length And (pattern[y] = wildcardAscNlazy Or pattern[y] = wildcardAscNgreedy) EndIf ' Check for exact match for non-special character ch = matchChar( pattern, y, str, x ) If ch x :+ 1; y :+ ch ' Evaluate . ElseIf pattern[y] = wildcardAscNlazy y :+ 1 If y >= pattern.length Return True While x < str.length And y < pattern.length ch = matchChar( pattern, y, str, x ) If ch Exit Else x :+ 1 Wend ' Evaluate % ElseIf pattern[y] = wildcardAscPlazy y :+ 1; x :+ 1 If y >= pattern.length Return True While x < str.length And y < pattern.length ch = matchChar( pattern, y, str, x ) If ch Exit Else x :+ 1 Wend ' Evaluate * ElseIf pattern[y] = wildcardAscNgreedy y :+ 1 If y >= pattern.length Return True While x < str.length And y < pattern.length ch = matchChar( pattern, y, str, x ) If ch And matchWild( pattern, str, True, x, y ) Return True Else x :+ 1 Wend ' Evaluate + ElseIf pattern[y] = wildcardAscPgreedy y :+ 1; x :+ 1 If y >= pattern.length Return True While x < str.length And y < pattern.length ch = matchChar( pattern, y, str, x ) If ch And matchWild( pattern, str, True, x, y ) Return True Else x :+ 1 Wend ' Not a match Else Return False EndIf Forever End Function |
Comments
| ||
[bookmarked] This could be quite useful, thanks. |
Code Archives Forum