If you want to avoid regular expressions altogether and need to support only that single case illustrated in your post, then this should work:
def match(string, pattern):
if len(string) != len(pattern):
return False
for s, p in zip(string, pattern):
if s != p and p != '?':
return False
return True
Execution example:
>>> match('ABC', 'ABC')
True
>>> match('ABC', 'AbC')
False
>>> match('ABC', 'A?C')
True
Explanation:
-
You may iterate over a string, with each iteration yielding another character:
>>> [i for i in 'ABC'] ['A', 'B', 'C'] -
zipallows you to iterate over both lists together:>>> [(s, p) for (s, p) in zip('STRING1', 'string2')] [('S', 's'), ('T', 't'), ('R', 'r'), ('I', 'i'), ('N', 'n'), ('G', 'g'), ('1', '2')]
A few remarks regarding your code snippet:
- There is no need to convert the input strings to
strsince they are guaranteed to be of that type. - Check for
TrueandFalsevalues using theisoperator. As @PadraicCunningham commented, just checkingif m(without== Trueoris True) would probably suffice in this case, since the value returned is certainly eitherTrueorFalse, but I prefer being explicit nonetheless. See this for more details. - Your
whileloop either runs forever or never. Your test ofpattern <= stringdoes not change throughout the loop and compares them lexicographically. - When using slicing to access just part of an iterable, there is no need to explicitly mention it’s beginning if it starts from 0, e.g.
string[0:len(pattern)]is equivalent tostring[:len(pattern)]. - You have incremented the
indexvariable without initializing it first, and you don’t use it anywhere else. -
The previous comment is no longer relevant, since you’ve updated your code, but your new statement doesn’t make any sense either.
Pythondoesn’t have built-in support for adding anintto astr, since it’s not well-defined. Executing that line of code will throw the following exception:>>> 'ABC'+1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: cannot concatenate 'str' and 'int' objectsFurthermore, even if it would have returned some value, you can’t just set it into an
strobject sincestrobjects are immutable (in the following example -1 denotes the length of the iterable minus 1):>>> x = 'ABCDE' >>> x[1:-1] = '123' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object does not support item assignmentWith lists, it would work:
>>> x = ['A', 'B', 'C', 'D', 'E'] >>> x[1:-1] = [1, 2, 3, 4, 5] >>> x ['A', 1, 2, 3, 4, 5, 'E'] -
The previous comment is no longer relevant since you’ve updated your code again, but that update is flawed as well. Now you’re comparing an
intagainst astrin the test clause of yourwhileloop. In Python 3, this will always raise an exception (though, not in Python 2):>>> 'ABC' > 0 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: str() > int()
EDIT:
Following your comment on the actual desired behavior, here is an updated solution. In a nutshell, it’s similar to the previous one, but it uses a modified version of the match function that expects both inputs to be of the exact same length. match_all extracts such substrings from the original string variable and invokes match with each of these substrings along with the original pattern variable:
def match_all(string, pattern):
if len(string) < len(pattern):
return False
for index in range(0, len(string) - len(pattern) + 1):
if match(string[index:index + len(pattern)], pattern) is True:
return True
return False
def match(string, pattern):
for s, p in zip(string, pattern):
if s != p and p != '?':
return False
return True
Execution example:
>>> match_all('ABC', '????')
False
>>> match_all('ABC', '???')
True
>>> match_all('ABC', 'AB')
True
>>> match_all('ABC', 'BC')
True
>>> match_all('ABC', 'Ab')
False
>>> match_all('ABC', 'A?')
True
12
solved using “?” as a stand-in for a single letter