определить, соответствует ли регулярное выражение только строкам фиксированной длины

Есть ли способ определить, соответствует ли регулярное выражение только строкам фиксированной длины? Моя идея состояла бы в том, чтобы сканировать *, + и? Затем потребуется некоторая разумная логика для поиска {m,n}, где m!=n. Не нужно брать | с учетом оператора.
Небольшой пример: ^\d{4} имеет фиксированную длину; ^\d{4,5} или ^\d+ имеют переменную длину

Я использую PCRE.

Спасибо.

Пол Прет


person Paul Praet    schedule 02.09.2010    source источник
comment
С другим регулярным выражением? :)   -  person Ani    schedule 02.09.2010
comment
Безусловно, необходимо учитывать |. В конце концов, какова фиксированная длина регулярного выражения /ab|c/?   -  person Philip Potter    schedule 02.09.2010
comment
Не забывайте, что * \+ и \? нужно игнорировать. Также квадратные скобки, содержащие эти символы, например. [+*?]   -  person JeremyP    schedule 02.09.2010
comment
Регулярное выражение для анализа регулярного выражения? Теперь у вас три проблемы!   -  person DrAl    schedule 02.09.2010
comment
@KennyTM: да, конечно @Ani: Я тоже так думал. @Philip Potter: Действительно, хорошее замечание, но, как я уже говорил, нас не волнует оператор | @JeremeP: Да, хорошее замечание. Я также приму это во внимание.   -  person Paul Praet    schedule 02.09.2010


Ответы (3)


Что ж, вы могли бы использовать тот факт, что механизм регулярных выражений Python допускает только регулярные выражения фиксированной длины в утверждениях просмотра назад:

import re
regexes = [r".x{2}(abc|def)", # fixed
           r"a|bc",           # variable/finite
           r"(.)\1",          # fixed
           r".{0,3}",         # variable/finite
           r".*"]             # variable/infinite

for regex in regexes:
    try:
        r = re.compile("(?<=" + regex + ")")
    except:
        print("Not fixed length: {}".format(regex))
    else:
        print("Fixed length: {}".format(regex))

будет выводить

Fixed length: .x{2}(abc|def)
Not fixed length: a|bc
Fixed length: (.)\1
Not fixed length: .{0,3}
Not fixed length: .*

Я предполагаю, что само регулярное выражение действительно.

Теперь, как Python узнает, имеет ли регулярное выражение фиксированную длину или нет? Просто прочитайте исходный код — в sre_parse.py есть метод getwidth(), который возвращает кортеж, состоящий из наименьшей и наибольшей возможной длины, и если они не равны в утверждении просмотра назад, re.compile() вызовет ошибку. Метод getwidth() рекурсивно проходит через регулярное выражение:

def getwidth(self):
    # determine the width (min, max) for this subpattern
    if self.width:
        return self.width
    lo = hi = 0
    UNITCODES = (ANY, RANGE, IN, LITERAL, NOT_LITERAL, CATEGORY)
    REPEATCODES = (MIN_REPEAT, MAX_REPEAT)
    for op, av in self.data:
        if op is BRANCH:
            i = sys.maxsize
            j = 0
            for av in av[1]:
                l, h = av.getwidth()
                i = min(i, l)
                j = max(j, h)
            lo = lo + i
            hi = hi + j
        elif op is CALL:
            i, j = av.getwidth()
            lo = lo + i
            hi = hi + j
        elif op is SUBPATTERN:
            i, j = av[1].getwidth()
            lo = lo + i
            hi = hi + j
        elif op in REPEATCODES:
            i, j = av[2].getwidth()
            lo = lo + int(i) * av[0]
            hi = hi + int(j) * av[1]
        elif op in UNITCODES:
            lo = lo + 1
            hi = hi + 1
        elif op == SUCCESS:
            break
    self.width = int(min(lo, sys.maxsize)), int(min(hi, sys.maxsize))
    return self.width
person Tim Pietzcker    schedule 02.09.2010
comment
Пишу на C, использую библиотеку PCRE... В любом случае спасибо :-) - person Paul Praet; 02.09.2010

Просто для удовольствия.

Предполагая, что регулярное выражение, которое мы тестируем, поддерживает только +, *, ?, {m,n}, {n} и [...] (за исключением некоторого странного синтаксиса, такого как []] и [^]]). Тогда регулярное выражение имеет фиксированную длину, только если оно следует грамматике:

 REGEX     -> ELEMENT *
 ELEMENT   -> CHARACTER ( '{' ( \d+ ) ( ',' \1 )? '}' )?
 CHARACTER -> [^+*?\\\[] | '\\' . | '[' ( '\\' . | [^\\\]] )+ ']'

который можно переписать на PCRE как:

^(?:(?:[^+*?\\\[{]|\\.|\[(?:\\.|[^\\\]])+\])(?:\{(\d+)(?:,\1)?\})?)*$
person kennytm    schedule 02.09.2010
comment
Я признаю, что понятия не имею, что вы имеете в виду ;-) Вы имеете в виду, что само регулярное выражение должно соответствовать приведенному выше выражению? - person Paul Praet; 02.09.2010

Согласно regular-expressions.info, механизм PCRE поддерживает только регулярные выражения фиксированной длины и чередование внутри lookbehinds.

Поэтому, если у вас есть действительное регулярное выражение, окружите его (?<= и ) и посмотрите, компилируется ли оно. Тогда вы знаете, что это либо фиксированный размер, либо чередование регулярных выражений фиксированного размера.

Я не уверен насчет чего-то вроде a(b|cd)e - это определенно не фиксированный размер, но он все равно может скомпилироваться. Вам нужно попробовать (у меня не установлен C/PCRE).

person Tim Pietzcker    schedule 02.09.2010