У меня есть список шаблонов как
list_patterns = [': error:', ': warning:', 'cc1plus:', 'undefine reference to']
то, что я хочу сделать, должно произвести объединение всех их приводящих к регулярному выражению, которое соответствует каждому элементу в list_patterns
[но по-видимому не соответствует никакому ре не в list_patterns - msw]
re.compile(list_patterns)
Действительно ли это возможно?
Есть несколько способов сделать это. Самый простой из них:
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 ()
, чтобы избежать любых специальных символов регулярного выражения.
Теперь, какой из них вы будете использовать, зависит от вашего списка паттернов. Являются ли они регулярными выражениями и могут ли они считаться действительными? Если так, то первое, вероятно, будет уместным. Если это строки, используйте второй метод.
В первом случае все усложняется, поскольку объединение нескольких регулярных выражений может изменить группировку и вызвать другие непредвиденные побочные эффекты.
list_regexs = [re.compile(x) for x in list_patterns]
Как насчет
ptrn = re.compile('|'.join(re.escape(e) for e in list_patterns))
Примечание использование re.escape ()
, чтобы избежать непредвиденных последствий из-за наличия в некоторых строках символов вроде () [] |. + * и т.д. Если вы этого хотите, в противном случае пропустите escape ()
.
Это также зависит от того, как вы собираетесь «использовать» это выражение - только для поиска совпадения или вы хотите собрать соответствующие группы обратно?
Вам нужен шаблон, который соответствует любому элементу в списке? Не будет ли это просто:
': error:|: warning:|cc1plus:|undefine reference to'?
Или, в коде Python:
re.compile("|".join(list_patterns))
Клетус дал очень хороший ответ. Если, однако, одна из строк для сопоставления может быть подстрокой другой, тогда вы должны сначала отсортировать строки в обратном порядке, чтобы самые короткие совпадения не скрывали более длинные.
Если, как заметил Алекс, исходный постер хотел именно то, о чем он действительно просил, то более легким решением, чем использование перестановок, могло бы быть следующее:
(...)
. 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) )
регулярное выражение, которое соответствует каждому элементу в списке
Я вижу, что вы уже получили несколько ответов, основанных на предположении, что под "соответствует каждому элементу в списке" вы на самом деле подразумевали "соответствует любому элементу в списке" (ответы в вопросах основаны на операторе |
"или" регулярных выражений).
Если вы действительно хотите, чтобы 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)