Как найти все перекрывающиеся совпадения переменного размера?

Я хочу найти все подстроки «01», которые содержат цифру или более, используя регулярное выражение, т.е. я хочу получить (в любом порядке):

['0', '01', '1']

Проблема в том, что совпадения регулярных выражений обычно не выбирают перекрывающиеся подстроки:

>>> re.findall(r'\d+', '01')
['01']

Умный обходной путь (найденный здесь) включает использование просмотра вперед. Но это все равно неудовлетворительно, так как будет найдено только одно совпадение для каждой позиции в строке:

>>> re.findall(r'(?=(\d+))', '01')
['01', '1']

Единственный способ решить эту проблему, который я могу придумать, - это использовать приведенное выше решение и перебирать каждую возможную длину подстроки:

s = '01'
matches = []
for n in range(1, len(s) + 1):
    matches += re.findall(r'(?=(\d{%i}))' % n, s)

Есть ли лучший встроенный способ сделать это напрямую с помощью регулярного выражения? Или, может быть, регулярное выражение не подходит для этого?

Спасибо!


person Jeffery    schedule 06.12.2019    source источник
comment
Возможно, вот так (?=((\d)(\d))) regex101.com/r/sgmH6t/1   -  person The fourth bird    schedule 06.12.2019
comment
@Thefourthbird Это правда, что в данном конкретном случае это может сработать! Но если я изменю свой пример, чтобы найти все подстроки «012», которые содержат цифру или более, он не найдет подстроку длины 3.   -  person Jeffery    schedule 06.12.2019
comment
Решение: ideone. com/9dmdUQ   -  person Wiktor Stribiżew    schedule 06.12.2019
comment
@WiktorStribiżew Это тоже отличный способ, спасибо! Я предполагаю, что консенсус здесь заключается в том, что нет встроенного способа сделать это с помощью одного регулярного выражения (что ответило бы на мой первоначальный вопрос). Я прав?   -  person Jeffery    schedule 06.12.2019
comment
@Jeffery Только один вариант регулярного выражения может сделать это с чистым регулярным выражением: perl6. Остальные не могут сделать это из коробки.   -  person Wiktor Stribiżew    schedule 06.12.2019


Ответы (2)


Альтернативное решение для использования регулярных выражений, использующее этот ответ, адаптированный к Python 3 для получения всех подстрок:

Код:

def get_all_substrings(input_string):
    length = len(input_string)
    return [input_string[i:j+1] for i in range(length) for j in range(i,length)]

s = '01'

strings = [sub for sub in get_all_substrings(s) if any(x.isdigit() for x in sub)]

Результат:

>>> strings
['0', '01', '1']
>>> s = '0td1'
>>> [sub for sub in get_all_substrings(s) if any(x.isdigit() for x in sub)]
['0', '0t', '0td', '0td1', 'td1', 'd1', '1']
person CDJB    schedule 06.12.2019

Вы можете использовать простое регулярное выражение \d+, а затем создать набор мощности для каждого совпадения (за исключением нулевых наборов). Вот функция powerset, которую я написал:

import itertools

def powerset(container, min_length=0):
    """
    Generate the powerset of container.

    A powerset is the set of all subsets of a given set, but this
    function is more flexible with input types. Output is an iterator
    of tuples.

    min_length is set to 0 to include the empty set, but can be
    set to 1 to exclude it.
    """
    for i in range(min_length, len(container)+1):
        yield from itertools.combinations(container, i)


import re
s = '01 eggs 98'
matches = re.findall(r'\d+', s)
result = [''.join(x) for match in matches for x in powerset(match, 1)]
print(result)  # -> ['0', '1', '01', '9', '8', '98']
person wjandrea    schedule 06.12.2019