Чи можливо виявити дійсний регулярний вираз з іншим регулярним виразом? Якщо так, будь ласка, надайте приклад нижче.
Чи можливо виявити дійсний регулярний вираз з іншим регулярним виразом? Якщо так, будь ласка, надайте приклад нижче.
Відповіді:
/
^ # 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 вони повинні підтримувати це.
Без пробілів та коментарів:
/^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*)$/
.NET не підтримує рекурсію безпосередньо. (The (?1)
and (?R)
constructs.) Рекурсію потрібно було б перетворити на підрахунок збалансованих груп:
^ # start of string
(?:
(?: [^?+*{}()[\]\\|]+ # literals and ^, $
| \\. # escaped characters
| \[ (?: \^?\\. | \^[^\\] | [^\\^] ) # character classes
(?: [^\]\\]+ | \\. )* \]
| \( (?:\?[:=!]
| \?<[=!]
| \?>
| \?<[^\W\d]\w*>
| \?'[^\W\d]\w*'
)? # opening of group
(?<N>) # increment counter
| \) # closing of group
(?<-N>) # decrement counter
)
(?: (?:[?+*]|\{\d+(?:,\d*)?\}) [?+]? )? # quantifiers
| \| # alternative
)* # repeat content
$ # end of string
(?(N)(?!)) # fail if counter is non-zero.
Ущільнено:
^(?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>|\?<[^\W\d]\w*>|\?'[^\W\d]\w*')?(?<N>)|\)(?<-N>))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*$(?(N)(?!))
З коментарів:
Чи підтвердить це заміна та переклад?
Це підтвердить лише регулярну частину підстановок та перекладів. s/<this part>/.../
Теоретично неможливо зіставити всі дійсні регекс-граматики з регулярними виразами.
Можливо, якщо двигун regex підтримує рекурсію, таку як PCRE, але це вже не можна назвати регулярними виразами.
Дійсно, "рекурсивний регулярний вираз" - це не регулярний вираз. Але це часто прийняте розширення для двигунів-генексів ... Як не дивно, цей розширений регулярний вираз не відповідає розширеним регексам.
"У теорії теорія та практика однакові. На практиці вони ні". Майже кожен, хто знає регулярні вирази, знає, що регулярні вирази не підтримують рекурсії. Але PCRE та більшість інших реалізацій підтримують набагато більше, ніж основні регулярні вирази.
використовуючи цей скрипт оболонки в команді grep, він показує мені деяку помилку .. grep: Недійсний вміст {}. Я створюю скрипт, який міг би зібрати кодову базу, щоб знайти всі файли, що містять регулярні вирази
Цей шаблон використовує розширення, яке називається рекурсивними регулярними виразами. Це не підтримується ароматом регулярного вираження POSIX. Ви можете спробувати за допомогою перемикача -P, щоб увімкнути аромат регексу PCRE.
Сам Regex "не є звичайною мовою, а тому не може бути розбитий регулярним виразом ..."
Це справедливо для класичних регулярних виразів. Деякі сучасні реалізації дозволяють рекурсію, що перетворює його на мову, що не містить контексту, хоча це завдання є дещо багатослівним.
Я бачу, де ти відповідаєш
[]()/\
. та інші спеціальні символи регулярного виразів. Де ви дозволяєте неспеціальних символів? Схоже, це буде відповідати^(?:[\.]+)$
, але ні^abcdefg$
. Це дійсний регулярний вираз.
[^?+*{}()[\]\\|]
відповідатиме будь-якому окремому символу, не входить до жодної з інших конструкцій. Це включає в себе як буквальний ( a
- z
), а також деякі спеціальні символи ( ^
, $
, .
).
.{,1}
не має собі рівних Зміна на ^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$
відповідність. Зміна \d+
на\d*
[a-b-c]
.
Ні, якщо ви суворо говорите про регулярні вирази і не включаєте в себе реалізацію регулярних виразів, які є фактично граматиками без контексту.
Існує одне обмеження регулярних виразів, яке унеможливлює написання регулярних виразів, які відповідають усім і тільки регулярним виразам. Ви не можете співставити такі реалізації, як дужки, які є парними. Regexes використовує багато таких конструкцій, візьмемо []
для прикладу. Кожен раз, коли є, [
повинно бути відповідність ]
, що досить просто для регулярного виразів "\[.*\]"
.
Те, що унеможливлює регулярні вирази, - це те, що вони можуть бути вкладені. Як можна написати регулярний вираз, який відповідає вкладеним дужкам? Відповідь, ви не можете без нескінченно тривалого регексу. Ви можете зіставити будь-яку кількість вкладених дужок через грубу силу, але ви ніколи не зможете зіставити довільно довгий набір вкладених дужок.
Цю можливість часто називають підрахунком, оскільки ви рахуєте глибину гніздування. Зворотний вираз за визначенням не має можливості рахувати.
Я закінчив писати про це « Регулярні обмеження висловлювань ».
Хороше питання.
Справжні звичайні мови не можуть вирішити довільно глибоко вкладені круглі дужки. Якщо ваш алфавіт містить, '('
а ')'
мета - визначити, чи є рядки з них добре сформованими дужками. Оскільки це необхідна вимога до регулярних виразів, відповідь - ні.
Однак якщо ви послабите вимогу і додасте рекурсію, ви, ймовірно, можете це зробити. Причина полягає в тому, що рекурсія може виступати як стек, дозволяючи «рахувати» поточну глибину вкладення, натискаючи на цей стек.
Расс Кокс написав " Відповідність регулярних виразів може бути простим і швидким ", що є чудовим трактатом про реалізацію двигуна regex.
Ні, якщо ви використовуєте стандартні регулярні вирази.
Причина в тому, що ви не можете задовольнити накачувальну лему для звичайних мов. Насосна лема стверджує , що рядок , що належить мові «L» є регулярним , якщо існує таке число «N», що після поділу рядка на три підрядка x
, y
, z
таким чином, що |x|>=1 && |xy|<=N
ви можете повторити y
стільки раз , скільки ви хочете і весь рядок все ще буде належати L
.
Наслідком накачувальної леми є те, що ви не можете мати регулярних рядків у формі a^Nb^Mc^N
, тобто дві підрядки, що мають однакову довжину, розділені іншим рядком. Будь-яким способом ви розділите такі рядки x
, y
і z
ви не можете "накачати", y
не отримавши рядок з різною кількістю "a" та "c", тим самим залишаючи мову оригіналу. Так буває, наприклад, з дужками в регулярних виразах.
Хоча цілком можливо використовувати рекурсивний регулярний вираз, як опублікував MizardX, для подібних речей набагато корисніше аналізатор. Спочатку регекси використовувались із звичайними мовами, тому що рекурсивна або балансуюча групи - це лише патч.
Мова, яка визначає дійсні регулярні вирази, насправді є без контексту, граматикою, і ви повинні використовувати відповідний аналізатор для обробки. Ось приклад університетського проекту для аналізу простих регулярних виразів (без більшості конструкцій). Тут використовується JavaCC. І так, коментарі є іспанською мовою, хоча назви методів досить зрозумілі.
SKIP :
{
" "
| "\r"
| "\t"
| "\n"
}
TOKEN :
{
< DIGITO: ["0" - "9"] >
| < MAYUSCULA: ["A" - "Z"] >
| < MINUSCULA: ["a" - "z"] >
| < LAMBDA: "LAMBDA" >
| < VACIO: "VACIO" >
}
IRegularExpression Expression() :
{
IRegularExpression r;
}
{
r=Alternation() { return r; }
}
// Matchea disyunciones: ER | ER
IRegularExpression Alternation() :
{
IRegularExpression r1 = null, r2 = null;
}
{
r1=Concatenation() ( "|" r2=Alternation() )?
{
if (r2 == null) {
return r1;
} else {
return createAlternation(r1,r2);
}
}
}
// Matchea concatenaciones: ER.ER
IRegularExpression Concatenation() :
{
IRegularExpression r1 = null, r2 = null;
}
{
r1=Repetition() ( "." r2=Repetition() { r1 = createConcatenation(r1,r2); } )*
{ return r1; }
}
// Matchea repeticiones: ER*
IRegularExpression Repetition() :
{
IRegularExpression r;
}
{
r=Atom() ( "*" { r = createRepetition(r); } )*
{ return r; }
}
// Matchea regex atomicas: (ER), Terminal, Vacio, Lambda
IRegularExpression Atom() :
{
String t;
IRegularExpression r;
}
{
( "(" r=Expression() ")" {return r;})
| t=Terminal() { return createTerminal(t); }
| <LAMBDA> { return createLambda(); }
| <VACIO> { return createEmpty(); }
}
// Matchea un terminal (digito o minuscula) y devuelve su valor
String Terminal() :
{
Token t;
}
{
( t=<DIGITO> | t=<MINUSCULA> ) { return t.image; }
}
Ви можете подати регулярний вираз, до preg_match
якого повернеться помилковим, якщо регулярний вираз не є дійсним Не забудьте використовувати @
для придушення повідомлень про помилки:
@preg_match($regexToTest, '');
//
.Наступний приклад Пола Макгуайєра, який спочатку був із вікі-програми Pyparsing, але тепер доступний лише через машину Wayback , дає граматику для розбору деяких регулярних виразів для повернення набору відповідних рядків. Як такий, він відкидає ті повтори, які включають необмежені умови повторення, наприклад "+" та "*". Але це повинно дати вам уявлення про те, як структурувати аналізатор, який обробляє повторні.
#
# 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)
class CharacterRangeEmitter(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 not in seen )
def __str__(self):
return '['+self.charset+']'
def __repr__(self):
return '['+self.charset+']'
def makeGenerator(self):
def genChars():
for s in self.charset:
yield s
return genChars
class OptionalEmitter(object):
def __init__(self,expr):
self.expr = expr
def makeGenerator(self):
def optionalGen():
yield ""
for s in self.expr.makeGenerator()():
yield s
return optionalGen
class DotEmitter(object):
def makeGenerator(self):
def dotGen():
for c in printables:
yield c
return dotGen
class GroupEmitter(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
if self.exprs:
for s in recurseList(self.exprs):
yield s
return groupGen
class AlternativeEmitter(object):
def __init__(self,exprs):
self.exprs = exprs
def makeGenerator(self):
def altGen():
for e in self.exprs:
for s in e.makeGenerator()():
yield s
return altGen
class LiteralEmitter(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():
yield self.lit
return litGen
def handleRange(toks):
return CharacterRangeEmitter(srange(toks[0]))
def handleRepetition(toks):
toks=toks[0]
if toks[1] in "*+":
raise ParseFatalException("",0,"unbounded repetition operators not supported")
if toks[1] == "?":
return OptionalEmitter(toks[0])
if "count" in toks:
return GroupEmitter([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]))
return GroupEmitter([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
return LiteralEmitter(lit)
def handleMacro(toks):
macroChar = toks[0][1]
if macroChar == "d":
return CharacterRangeEmitter("0123456789")
elif macroChar == "w":
return CharacterRangeEmitter(srange("[A-Za-z0-9_]"))
elif macroChar == "s":
return LiteralEmitter(" ")
else:
raise ParseFatalException("",0,"unsupported macro character (" + macroChar + ")")
def handleSequence(toks):
return GroupEmitter(toks[0])
def handleDot():
return CharacterRangeEmitter(printables)
def handleAlternative(toks):
return AlternativeEmitter(toks[0])
_parser = None
def parser():
global _parser
if _parser is None:
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 not in 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 = 0
for s in gen:
i += 1
return 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()
if not t: continue
print '-'*50
print t
try:
print count(invert(t))
for s in invert(t):
print s
except ParseFatalException,pfe:
print pfe.msg
print
continue
print
if __name__ == "__main__":
main()