Python: определение объединения регулярных выражений

У меня есть список шаблонов как

list_patterns = [': error:', ': warning:', 'cc1plus:', 'undefine reference to']

то, что я хочу сделать, должно произвести объединение всех их приводящих к регулярному выражению, которое соответствует каждому элементу в list_patterns [но по-видимому не соответствует никакому ре не в list_patterns - msw]

re.compile(list_patterns)

Действительно ли это возможно?

5
задан paxdiablo 18 July 2010 в 02:25
поделиться

6 ответов

Есть несколько способов сделать это. Самый простой из них:

list_patterns = [': error:', ': warning:', 'cc1plus:', 'undefine reference to']
string = 'there is an : error: and a cc1plus: in this string'
print re.findall('|'.join(list_patterns), string)

Вывод:

[': error:', 'cc1plus:']

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

list_patterns = [': error:', ': warning:', 'cc1plus:', 'undefine reference to']
string = 'there is an : error: and a cc1plus: in this string'
pattern = "|".join(re.escape(p) for p in list_patterns)
print re.findall(pattern, string)

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

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

В первом случае все усложняется, поскольку объединение нескольких регулярных выражений может изменить группировку и вызвать другие непредвиденные побочные эффекты.

10
ответ дан 18 December 2019 в 09:47
поделиться
list_regexs = [re.compile(x) for x in list_patterns]
3
ответ дан 18 December 2019 в 09:47
поделиться

Как насчет

ptrn = re.compile('|'.join(re.escape(e) for e in list_patterns))

Примечание использование re.escape () , чтобы избежать непредвиденных последствий из-за наличия в некоторых строках символов вроде () [] |. + * и т.д. Если вы этого хотите, в противном случае пропустите escape () .

Это также зависит от того, как вы собираетесь «использовать» это выражение - только для поиска совпадения или вы хотите собрать соответствующие группы обратно?

2
ответ дан 18 December 2019 в 09:47
поделиться

Вам нужен шаблон, который соответствует любому элементу в списке? Не будет ли это просто:

': error:|: warning:|cc1plus:|undefine reference to'?

Или, в коде Python:

re.compile("|".join(list_patterns))
1
ответ дан 18 December 2019 в 09:47
поделиться

Клетус дал очень хороший ответ. Если, однако, одна из строк для сопоставления может быть подстрокой другой, тогда вы должны сначала отсортировать строки в обратном порядке, чтобы самые короткие совпадения не скрывали более длинные.

Если, как заметил Алекс, исходный постер хотел именно то, о чем он действительно просил, то более легким решением, чем использование перестановок, могло бы быть следующее:

  • Удаление любых дубликатов в list_patterns. (Лучше было бы начать с набора, а затем превратить его в список с обратной сортировкой без дубликатов).
  • re.escape () элементы списка.
  • Окружите каждый элемент в отдельности группой (...) .
  • '|' .join () все группы.
  • Найдите набор индексов всех совпавших групп и сравните его длину с len (list_patterns) .

Если есть хотя бы одно совпадение для каждой записи в исходном списке строк, то длина набора должна совпадать.

Код будет примерно таким:

import re

def usedgroupindex(indexabledata):
    for i,datum in enumerate(indexabledata):
        if datum: return i
    # return None

def findallstrings(list_patterns, string):
    lp = sorted(set(list_patterns), reverse=True)
    pattern = "|".join("(%s)" % re.escape(p) for p in lp)
    # for m in re.findall(pattern, string): print (m, usedgroupindex(m))
    return ( len(set(usedgroupindex(m) for m in re.findall(pattern, string)))
             == len(lp) )

list_patterns = [': error:', ': warning:', 'cc1plus:', 'undefine reference to']
string = ' XZX '.join(list_patterns)

print ( findallstrings(list_patterns, string) )
1
ответ дан 18 December 2019 в 09:47
поделиться

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

Я вижу, что вы уже получили несколько ответов, основанных на предположении, что под "соответствует каждому элементу в списке" вы на самом деле подразумевали "соответствует любому элементу в списке" (ответы в вопросах основаны на операторе | "или" регулярных выражений).

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

Для сопоставления в порядке, '.*?'.join(list_patterns) должен служить вам хорошо (если элементы действительно должны быть взяты как шаблоны RE - если они должны быть взяты как буквальные строки вместо этого, '.*?'.join(re.escape(p) для p list_patterns)).

Для сопоставления в любом порядке регулярные выражения сами по себе не предлагают прямой поддержки. Можно взять все перестановки списка (например, с помощью itertools.permutations), объединить каждую из них с помощью '.*?', и соединить все вместе с помощью | - но это может привести к ужасно длинному шаблону RE, потому что число перестановок N элементов равно N! ("факториал N" -- например, для N равного 4, перестановок будет 4 * 3 * 2 * 1 == 24). Поэтому производительность может легко пострадать, если только не известно, что количество элементов в списке очень, очень мало.

Для более общего решения проблемы "сопоставления каждого элемента в произвольном порядке" (если это то, что вам нужно), с производительностью и объемом памяти, которые все еще приемлемы для прилично больших длин списков, вам нужно отказаться от цели заставить все это работать с одним объектом RE, и ввести некоторую логику в смесь - например, сделать список объектов RE с relist=[re. compile(p) for p in list_patterns], и проверить "все они соответствуют строке s, в любом порядке" с помощью all(r.search(s) for r in relist) или подобным образом.

Конечно, если вам нужно заставить этот последний подход работать "совместимым с утиной типизацией способом" с реальными объектами RE, это несложно, например, если все, что вам нужно - это метод search, который возвращает булевый результат (поскольку возвращать "объект соответствия" не имеет смысла)...:

class relike(object):
    def __init__(self, list_patterns):
        self.relist = [re.compile(p) for p in list_patterns]
    def search(self, s):
        return all(r.search(s) for r in relist)
0
ответ дан 18 December 2019 в 09:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: