[Solved] using “?” as a stand-in for a single letter


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:

  1. You may iterate over a string, with each iteration yielding another character:

    >>> [i for i in 'ABC']
    ['A', 'B', 'C']
    
  2. 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:

  1. There is no need to convert the input strings to str since they are guaranteed to be of that type.
  2. Check for True and False values using the is operator. As @PadraicCunningham commented, just checking if m (without == True or is True) would probably suffice in this case, since the value returned is certainly either True or False, but I prefer being explicit nonetheless. See this for more details.
  3. Your while loop either runs forever or never. Your test of pattern <= string does not change throughout the loop and compares them lexicographically.
  4. 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 to string[:len(pattern)].
  5. You have incremented the index variable without initializing it first, and you don’t use it anywhere else.
  6. 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 an int to a str, 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 since str 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']
    
  7. 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 a str in the test clause of your while 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