Итак, ваша проблема заключается в проверке регулярного выражения, вы выбрали регулярное выражение для его решения. Интересно, является ли свойство регулярного выражения увеличения числа задач аддитивным или мультипликативным? Похоже, 4 проблемы вместо 2 :)
abesto
15
Для регулярных выражений существует множество обозначений - некоторые функции и их написание являются общими для большинства, некоторые пишутся по-разному или доступны только в одной конкретной записи. Большинство этих нотаций не являются «регулярными» в смысле обычной грамматики - вам нужен контекстно-свободный синтаксический анализатор для обработки неограниченного вложения подвыражений - хотя многие современные нотации «регулярных выражений» имеют расширения, которые выходят за рамки исходного формального определения и может позволить их собственные обозначения, которые будут признаны. В любом случае, почему бы просто не спросить свою библиотеку регулярных выражений, допустимо ли каждое регулярное выражение?
Steve314
1
@bevacqua Мне нужно проверить регулярное выражение в схеме XML. Как я могу сделать это без другого регулярного выражения?
zenden2k 9.09.15
3
На самом деле скомпилируйте / запустите регулярное выражение (шаблон) для проверки в соответствии с механизмом обработки исключений, который есть в вашем языке. Таким образом, движок / компилятор языка regex сам проверит это. (Это предполагает правильный базовый синтаксис, так что программа запускается, но это можно включить в проверку, используя возможности ваших языков для оценки строки для регулярного выражения в виде (возможно, синтаксически неправильного) кода, или такого.)
/^# start of string(# first group start(?:(?:[^?+*{}()[\]\\|]+# literals and ^, $| \\.# escaped characters| \[ (?: \^?\\.| \^[^\\]|[^\\^])# character classes(?:[^\]\\]+| \\.)* \]
| \( (?:\?[:=!]|\?<[=!]|\?>)?(?1)?? \) # parenthesis, with recursive content| \(\? (?:R|[+-]?\d+) \) # recursive matching)(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?# quantifiers| \| # alternative)*# repeat content)# end first group
$ # end of string/
Это рекурсивное регулярное выражение, которое не поддерживается многими механизмами регулярных выражений. Основанные на PCRE должны поддержать это.
Это будет проверять только часть регулярных выражений замен и переводов. s/<this part>/.../
Теоретически невозможно сопоставить все действительные грамматики регулярных выражений с регулярными выражениями.
Это возможно, если механизм регулярных выражений поддерживает рекурсию, такую как PCRE, но это уже нельзя назвать регулярными выражениями.
Действительно, «рекурсивное регулярное выражение» не является регулярным выражением. Но это часто принимаемое расширение для механизмов регулярных выражений ... Как ни странно, это расширенное регулярное выражение не соответствует расширенным регулярным выражениям.
«Теоретически, теория и практика - это одно и то же. На практике это не так». Почти каждый, кто знает регулярные выражения, знает, что регулярные выражения не поддерживают рекурсию. Но PCRE и большинство других реализаций поддерживают гораздо больше, чем базовые регулярные выражения.
используя это со сценарием оболочки в команде grep, он показывает некоторую ошибку .. grep: недопустимое содержимое {}. Я делаю сценарий, который может grep кода базы, чтобы найти все файлы, которые содержат регулярные выражения
Этот шаблон использует расширение, называемое рекурсивными регулярными выражениями. Это не поддерживается разновидностью регулярных выражений POSIX. Вы можете попробовать с ключом -P включить режим регулярных выражений PCRE.
Само регулярное выражение "не является регулярным языком и, следовательно, не может быть проанализировано с помощью регулярного выражения ..."
Это верно для классических регулярных выражений. В некоторых современных реализациях допускается рекурсия, что делает его языком без контекста, хотя это несколько многословно для этой задачи.
Я вижу, где вы подходите []()/\. и другие специальные символы регулярных выражений. Где вы разрешаете не специальные символы? Кажется, что это будет соответствовать ^(?:[\.]+)$, но нет ^abcdefg$. Это действительное регулярное выражение.
[^?+*{}()[\]\\|]будет соответствовать любому отдельному символу, не являющемуся частью какой-либо другой конструкции. Это включает в себя как литерал ( a- z), а также некоторые специальные символы ( ^, $, .).
Этот ответ отправляет людей в совершенно неверном направлении. Они никогда не должны использовать regEx для поиска регулярных выражений, потому что это не может работать правильно во всех случаях. Смотрите мой ответ добавлен.
vitaly-t
1
.{,1}не имеет себе равных. Изменить на ^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$совпадения. Изменить \d+на\d*
yunzen
4
Регулярное выражение по def не должно иметь рекурсии, по крайней мере, сказать что-то подобное в вашем ответе, ваш движок регулярных выражений, вероятно, "слишком мощный" и на самом деле не является движком регулярных выражений.
Чарли Паркер
Просто заметка, что вы забыли флаг x
RedClover
Этот валидатор, похоже, создан для выражений PCRE, но он пропустит много недопустимых ERE POSIX. Примечательно, что они немного разборчивее в диапазоне классов символов, например , это справедливо в PCRE , но не в ЭРД: [a-b-c].
Нет, если вы строго говорите о регулярных выражениях и не учитываете некоторые реализации регулярных выражений, которые на самом деле являются контекстно-свободными грамматиками.
Существует одно ограничение регулярных выражений, которое делает невозможным написание регулярного выражения, которое соответствует всем и только регулярным выражениям. Вы не можете сопоставить реализации, такие как фигурные скобки, которые являются парными. Регулярные выражения используют много таких конструкций, давайте возьмем []в качестве примера. Всякий раз, когда есть, [должно быть соответствие ], которое достаточно просто для регулярного выражения "\[.*\]".
Что делает невозможным для регулярных выражений то, что они могут быть вложенными. Как вы можете написать регулярное выражение, которое соответствует вложенным скобкам? Ответ в том, что вы не можете без бесконечно длинного регулярного выражения. Вы можете сопоставить любое количество вложенных скобок с помощью грубой силы, но вы никогда не сможете сопоставить произвольно длинный набор вложенных скобок.
Эту возможность часто называют подсчетом, потому что вы подсчитываете глубину вложения. Регулярное выражение по определению не имеет возможности считать.
Истинные регулярные языки не могут решить сколь угодно глубоко вложенные правильно сформированные скобки. Если ваш алфавит содержит '('и ')'цель состоит в том, чтобы решить, имеет ли строка из них правильно сформированные совпадающие скобки. Поскольку это является обязательным требованием для регулярных выражений, ответ - нет.
Однако, если вы ослабите требование и добавите рекурсию, вы, вероятно, сможете это сделать. Причина в том, что рекурсия может действовать как стек, позволяя вам «посчитать» текущую глубину вложения, помещая ее в этот стек.
Нет, если вы используете стандартные регулярные выражения.
Причина в том, что вы не можете удовлетворить лемму прокачки для обычных языков. Насосная лемма гласит, что строка, принадлежащая языку «L», является правильной, если существует число «N», такое, что после разделения строки на три подстроки x, так y, zчто |x|>=1 && |xy|<=Nвы можете повторять yстолько раз, сколько захотите, и вся строка будет по-прежнему принадлежать L.
Следствием леммы прокачки является то, что вы не можете иметь регулярные строки в форме a^Nb^Mc^N, то есть две подстроки одинаковой длины, разделенные другой строкой. В любом случае вы разбиваете такие строки на x, yи zвы не можете «качать», yне получив строку с другим числом «a» и «c», оставляя тем самым исходный язык. Так обстоит дело, например, с круглыми скобками в регулярных выражениях.
Это не очень точное описание леммы прокачки. Во-первых, это может быть обычный язык или нет, а не одна строка. Во-вторых, это необходимое, а не достаточное условие регулярности. Наконец, прокачиваются только достаточно длинные струны.
Дарий Гринберг
13
Хотя вполне возможно использовать рекурсивное регулярное выражение, как написал MizardX, для такого рода вещей гораздо более полезен парсер. Изначально регулярные выражения предназначались для использования с обычными языками, рекурсивность или наличие групп балансировки - это просто патч.
Язык, который определяет допустимые регулярные выражения, на самом деле является контекстно-свободной грамматикой, и вы должны использовать соответствующий анализатор для его обработки. Вот пример университетского проекта для анализа простых регулярных выражений (без большинства конструкций). Он использует JavaCC. И да, комментарии на испанском языке, хотя названия методов довольно понятны.
Вы можете отправить регулярное выражение, в preg_matchкоторое будет возвращено ложное значение, если регулярное выражение недействительно. Не забудьте использовать @для подавления сообщений об ошибках:
В следующем примере Пола Макгуайра, который изначально был написан на pyparsing wiki, но теперь доступен только через Wayback Machine , приведена грамматика для анализа некоторых регулярных выражений с целью возврата набора совпадающих строк. Как таковой, он отклоняет те, которые включают неограниченные термины повторения, такие как «+» и «*». Но это должно дать вам представление о том, как структурировать синтаксический анализатор, который будет обрабатывать его.
# # invRegex.py## Copyright 2008, Paul McGuire## pyparsing script to expand a regular expression into all possible matching strings# Supports:# - {n} and {m,n} repetition, but not unbounded + or * repetition# - ? optional elements# - [] character ranges# - () grouping# - | alternation#
__all__ =["count","invert"]from pyparsing import(Literal, oneOf, printables,ParserElement,Combine,SkipTo, operatorPrecedence,ParseFatalException,Word, nums, opAssoc,Suppress,ParseResults, srange)classCharacterRangeEmitter(object):def __init__(self,chars):# remove duplicate chars in character range, but preserve original order
seen =set()self.charset ="".join( seen.add(c)or c for c in chars if c notin seen )def __str__(self):return'['+self.charset+']'def __repr__(self):return'['+self.charset+']'def makeGenerator(self):def genChars():for s inself.charset:yield s
return genChars
classOptionalEmitter(object):def __init__(self,expr):self.expr = expr
def makeGenerator(self):def optionalGen():yield""for s inself.expr.makeGenerator()():yield s
return optionalGen
classDotEmitter(object):def makeGenerator(self):def dotGen():for c in printables:yield c
return dotGen
classGroupEmitter(object):def __init__(self,exprs):self.exprs =ParseResults(exprs)def makeGenerator(self):def groupGen():def recurseList(elist):if len(elist)==1:for s in elist[0].makeGenerator()():yield s
else:for s in elist[0].makeGenerator()():for s2 in recurseList(elist[1:]):yield s + s2
ifself.exprs:for s in recurseList(self.exprs):yield s
return groupGen
classAlternativeEmitter(object):def __init__(self,exprs):self.exprs = exprs
def makeGenerator(self):def altGen():for e inself.exprs:for s in e.makeGenerator()():yield s
return altGen
classLiteralEmitter(object):def __init__(self,lit):self.lit = lit
def __str__(self):return"Lit:"+self.lit
def __repr__(self):return"Lit:"+self.lit
def makeGenerator(self):def litGen():yieldself.lit
return litGen
def handleRange(toks):returnCharacterRangeEmitter(srange(toks[0]))def handleRepetition(toks):
toks=toks[0]if toks[1]in"*+":raiseParseFatalException("",0,"unbounded repetition operators not supported")if toks[1]=="?":returnOptionalEmitter(toks[0])if"count"in toks:returnGroupEmitter([toks[0]]*int(toks.count))if"minCount"in toks:
mincount =int(toks.minCount)
maxcount =int(toks.maxCount)
optcount = maxcount - mincount
if optcount:
opt =OptionalEmitter(toks[0])for i in range(1,optcount):
opt =OptionalEmitter(GroupEmitter([toks[0],opt]))returnGroupEmitter([toks[0]]* mincount +[opt])else:return[toks[0]]* mincount
def handleLiteral(toks):
lit =""for t in toks:if t[0]=="\\":if t[1]=="t":
lit +='\t'else:
lit += t[1]else:
lit += t
returnLiteralEmitter(lit)def handleMacro(toks):
macroChar = toks[0][1]if macroChar =="d":returnCharacterRangeEmitter("0123456789")elif macroChar =="w":returnCharacterRangeEmitter(srange("[A-Za-z0-9_]"))elif macroChar =="s":returnLiteralEmitter(" ")else:raiseParseFatalException("",0,"unsupported macro character ("+ macroChar +")")def handleSequence(toks):returnGroupEmitter(toks[0])def handleDot():returnCharacterRangeEmitter(printables)def handleAlternative(toks):returnAlternativeEmitter(toks[0])
_parser =Nonedef parser():global _parser
if _parser isNone:ParserElement.setDefaultWhitespaceChars("")
lbrack,rbrack,lbrace,rbrace,lparen,rparen = map(Literal,"[]{}()")
reMacro =Combine("\\"+ oneOf(list("dws")))
escapedChar =~reMacro +Combine("\\"+ oneOf(list(printables)))
reLiteralChar ="".join(c for c in printables if c notin r"\[]{}().*?+|")+" \t"
reRange =Combine(lbrack +SkipTo(rbrack,ignore=escapedChar)+ rbrack)
reLiteral =( escapedChar | oneOf(list(reLiteralChar)))
reDot =Literal(".")
repetition =(( lbrace +Word(nums).setResultsName("count")+ rbrace )|( lbrace +Word(nums).setResultsName("minCount")+","+Word(nums).setResultsName("maxCount")+ rbrace )|
oneOf(list("*+?")))
reRange.setParseAction(handleRange)
reLiteral.setParseAction(handleLiteral)
reMacro.setParseAction(handleMacro)
reDot.setParseAction(handleDot)
reTerm =( reLiteral | reRange | reMacro | reDot )
reExpr = operatorPrecedence( reTerm,[(repetition,1, opAssoc.LEFT, handleRepetition),(None,2, opAssoc.LEFT, handleSequence),(Suppress('|'),2, opAssoc.LEFT, handleAlternative),])
_parser = reExpr
return _parser
def count(gen):"""Simple function to count the number of elements returned by a generator."""
i =0for s in gen:
i +=1return i
def invert(regex):"""Call this routine as a generator to return all the strings that
match the input regular expression.
for s in invert("[A-Z]{3}\d{3}"):
print s
"""
invReGenerator =GroupEmitter(parser().parseString(regex)).makeGenerator()return invReGenerator()def main():
tests = r"""
[A-EA]
[A-D]*
[A-D]{3}
X[A-C]{3}Y
X[A-C]{3}\(
X\d
foobar\d\d
foobar{2}
foobar{2,9}
fooba[rz]{2}
(foobar){2}
([01]\d)|(2[0-5])
([01]\d\d)|(2[0-4]\d)|(25[0-5])
[A-C]{1,2}
[A-C]{0,3}
[A-C]\s[A-C]\s[A-C]
[A-C]\s?[A-C][A-C]
[A-C]\s([A-C][A-C])
[A-C]\s([A-C][A-C])?
[A-C]{2}\d{2}
@|TH[12]
@(@|TH[12])?
@(@|TH[12]|AL[12]|SP[123]|TB(1[0-9]?|20?|[3-9]))?
@(@|TH[12]|AL[12]|SP[123]|TB(1[0-9]?|20?|[3-9])|OH(1[0-9]?|2[0-9]?|30?|[4-9]))?
(([ECMP]|HA|AK)[SD]|HS)T
[A-CV]{2}
A[cglmrstu]|B[aehikr]?|C[adeflmorsu]?|D[bsy]|E[rsu]|F[emr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airu]|M[dgnot]|N[abdeiop]?|Os?|P[abdmortu]?|R[abefghnu]|S[bcegimnr]?|T[abcehilm]|Uu[bhopqst]|U|V|W|Xe|Yb?|Z[nr]
(a|b)|(x|y)
(a|b) (x|y)
""".split('\n')for t in tests:
t = t.strip()ifnot t:continueprint'-'*50print t
try:print count(invert(t))for s in invert(t):print s
exceptParseFatalException,pfe:print pfe.msg
printcontinueprintif __name__ =="__main__":
main()
Ответы:
Это рекурсивное регулярное выражение, которое не поддерживается многими механизмами регулярных выражений. Основанные на PCRE должны поддержать это.
Без пробелов и комментариев:
.NET не поддерживает рекурсию напрямую. (Конструкции
(?1)
и(?R)
.) Рекурсия должна быть преобразована в подсчет сбалансированных групп:Уплотненный:
Из комментариев:
Это будет проверять только часть регулярных выражений замен и переводов.
s/<this part>/.../
Это возможно, если механизм регулярных выражений поддерживает рекурсию, такую как PCRE, но это уже нельзя назвать регулярными выражениями.
«Теоретически, теория и практика - это одно и то же. На практике это не так». Почти каждый, кто знает регулярные выражения, знает, что регулярные выражения не поддерживают рекурсию. Но PCRE и большинство других реализаций поддерживают гораздо больше, чем базовые регулярные выражения.
Этот шаблон использует расширение, называемое рекурсивными регулярными выражениями. Это не поддерживается разновидностью регулярных выражений POSIX. Вы можете попробовать с ключом -P включить режим регулярных выражений PCRE.
Это верно для классических регулярных выражений. В некоторых современных реализациях допускается рекурсия, что делает его языком без контекста, хотя это несколько многословно для этой задачи.
[^?+*{}()[\]\\|]
будет соответствовать любому отдельному символу, не являющемуся частью какой-либо другой конструкции. Это включает в себя как литерал (a
-z
), а также некоторые специальные символы (^
,$
,.
).источник
.{,1}
не имеет себе равных. Изменить на^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$
совпадения. Изменить\d+
на\d*
[a-b-c]
.Вряд ли.
Оцените это на
try..catch
любом другом языке.источник
Нет, если вы строго говорите о регулярных выражениях и не учитываете некоторые реализации регулярных выражений, которые на самом деле являются контекстно-свободными грамматиками.
Существует одно ограничение регулярных выражений, которое делает невозможным написание регулярного выражения, которое соответствует всем и только регулярным выражениям. Вы не можете сопоставить реализации, такие как фигурные скобки, которые являются парными. Регулярные выражения используют много таких конструкций, давайте возьмем
[]
в качестве примера. Всякий раз, когда есть,[
должно быть соответствие]
, которое достаточно просто для регулярного выражения"\[.*\]"
.Что делает невозможным для регулярных выражений то, что они могут быть вложенными. Как вы можете написать регулярное выражение, которое соответствует вложенным скобкам? Ответ в том, что вы не можете без бесконечно длинного регулярного выражения. Вы можете сопоставить любое количество вложенных скобок с помощью грубой силы, но вы никогда не сможете сопоставить произвольно длинный набор вложенных скобок.
Эту возможность часто называют подсчетом, потому что вы подсчитываете глубину вложения. Регулярное выражение по определению не имеет возможности считать.
Я закончил тем, что написал « Ограничения регулярного выражения » об этом.
источник
Хороший вопрос.
Истинные регулярные языки не могут решить сколь угодно глубоко вложенные правильно сформированные скобки. Если ваш алфавит содержит
'('
и')'
цель состоит в том, чтобы решить, имеет ли строка из них правильно сформированные совпадающие скобки. Поскольку это является обязательным требованием для регулярных выражений, ответ - нет.Однако, если вы ослабите требование и добавите рекурсию, вы, вероятно, сможете это сделать. Причина в том, что рекурсия может действовать как стек, позволяя вам «посчитать» текущую глубину вложения, помещая ее в этот стек.
Рассказ Кокса (Russ Cox) написал « Сопоставление регулярных выражений может быть простым и быстрым », который представляет собой замечательный трактат о реализации движка regex.
источник
Нет, если вы используете стандартные регулярные выражения.
Причина в том, что вы не можете удовлетворить лемму прокачки для обычных языков. Насосная лемма гласит, что строка, принадлежащая языку «L», является правильной, если существует число «N», такое, что после разделения строки на три подстроки
x
, такy
,z
что|x|>=1 && |xy|<=N
вы можете повторятьy
столько раз, сколько захотите, и вся строка будет по-прежнему принадлежатьL
.Следствием леммы прокачки является то, что вы не можете иметь регулярные строки в форме
a^Nb^Mc^N
, то есть две подстроки одинаковой длины, разделенные другой строкой. В любом случае вы разбиваете такие строки наx
,y
иz
вы не можете «качать»,y
не получив строку с другим числом «a» и «c», оставляя тем самым исходный язык. Так обстоит дело, например, с круглыми скобками в регулярных выражениях.источник
Хотя вполне возможно использовать рекурсивное регулярное выражение, как написал MizardX, для такого рода вещей гораздо более полезен парсер. Изначально регулярные выражения предназначались для использования с обычными языками, рекурсивность или наличие групп балансировки - это просто патч.
Язык, который определяет допустимые регулярные выражения, на самом деле является контекстно-свободной грамматикой, и вы должны использовать соответствующий анализатор для его обработки. Вот пример университетского проекта для анализа простых регулярных выражений (без большинства конструкций). Он использует JavaCC. И да, комментарии на испанском языке, хотя названия методов довольно понятны.
источник
Вы можете отправить регулярное выражение, в
preg_match
которое будет возвращено ложное значение, если регулярное выражение недействительно. Не забудьте использовать@
для подавления сообщений об ошибках://
.источник
В следующем примере Пола Макгуайра, который изначально был написан на pyparsing wiki, но теперь доступен только через Wayback Machine , приведена грамматика для анализа некоторых регулярных выражений с целью возврата набора совпадающих строк. Как таковой, он отклоняет те, которые включают неограниченные термины повторения, такие как «+» и «*». Но это должно дать вам представление о том, как структурировать синтаксический анализатор, который будет обрабатывать его.
источник