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']
-
zip
allows 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
str
since they are guaranteed to be of that type. - Check for
True
andFalse
values using theis
operator. As @PadraicCunningham commented, just checkingif m
(without== True
oris True
) would probably suffice in this case, since the value returned is certainly eitherTrue
orFalse
, but I prefer being explicit nonetheless. See this for more details. - Your
while
loop either runs forever or never. Your test ofpattern <= string
does 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
index
variable 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.
Python
doesn’t have built-in support for adding anint
to 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' objects
Furthermore, even if it would have returned some value, you can’t just set it into an
str
object sincestr
objects 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 assignment
With 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
int
against astr
in the test clause of yourwhile
loop. 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