是否可以使用另一个正则表达式检测有效的正则表达式?如果是这样,请在下面给出示例代码。
/
^ # 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 不直接支持递归。 ((?1)
和 (?R)
构造。)必须将递归转换为计数平衡组:
^ # 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>/.../
理论上不可能将所有有效的正则表达式语法与正则表达式匹配。
如果正则表达式引擎支持递归是可能的,例如 PCRE,但它不能再被称为正则表达式了。
实际上,“递归正则表达式”不是正则表达式。但这是一个经常被接受的正则表达式引擎扩展......具有讽刺意味的是,这个扩展的正则表达式与扩展的正则表达式不匹配。
“理论上,理论和实践是一样的。在实践中,它们不是。”几乎所有了解正则表达式的人都知道,正则表达式不支持递归。但是 PCRE 和大多数其他实现支持的远不止基本的正则表达式。
在 grep 命令中将此与 shell 脚本一起使用,它向我显示了一些错误.. grep: Invalid content of {} 。我正在制作一个可以 grep 代码库以查找所有包含正则表达式的文件的脚本
这种模式利用了一个称为递归正则表达式的扩展。这不受 POSIX 风格的正则表达式支持。您可以尝试使用 -P 开关来启用 PCRE 正则表达式风格。
正则表达式本身“不是正则语言,因此不能被正则表达式解析......”
这适用于经典正则表达式。一些现代实现允许递归,这使它成为一种无上下文的语言,尽管它对于这个任务来说有点冗长。
我看到你在哪里匹配 []()/\。和其他特殊的正则表达式字符。你在哪里允许非特殊字符?看起来这将匹配 ^(?:[\.]+)$,但不匹配 ^abcdefg$。这是一个有效的正则表达式。
[^?+*{}()[\]\\|]
将匹配任何单个字符,而不是任何其他构造的一部分。这包括文字(a
- z
)和某些特殊字符(^
、$
、.
)。
不太可能。
在 try..catch
或您的语言提供的任何内容中对其进行评估。
不,如果您严格谈论正则表达式而不包括一些实际上是上下文无关语法的正则表达式实现。
正则表达式有一个限制,这使得无法编写匹配所有且仅匹配正则表达式的正则表达式。您不能匹配成对的大括号等实现。正则表达式使用许多这样的结构,我们以 []
为例。只要有 [
,就必须有一个匹配的 ]
,这对于正则表达式 "\[.*\]"
来说很简单。
正则表达式不可能的原因是它们可以嵌套。如何编写匹配嵌套括号的正则表达式?答案是你不能没有无限长的正则表达式。您可以通过蛮力匹配任意数量的嵌套括号,但您永远无法匹配任意长的嵌套括号集。
此功能通常称为计数,因为您正在计算嵌套的深度。根据定义,正则表达式没有计算能力。
我最终为此写了“Regular Expression Limitations”。
In a future (hopefully soon) post I will explore the recursive extensions to the .Net regular expression language.
) 中使用递归正则表达式写过这篇文章?
好问题。
真正的正则语言不能任意决定深度嵌套的格式良好的括号。如果您的字母表包含 '('
和 ')'
,则目标是确定其中的字符串是否具有格式正确的匹配括号。由于这是正则表达式的必要要求,因此答案是否定的。
但是,如果您放宽要求并添加递归,您可能可以做到。原因是递归可以充当堆栈,让您通过推入该堆栈来“计算”当前嵌套深度。
Russ Cox 写了“Regular Expression Matching Can Be Simple And Fast”,这是一篇关于正则表达式引擎实现的精彩论文。
不,如果您使用标准正则表达式。
原因是您不能满足常规语言的 pumping lemma。抽水引理指出,如果存在数字“N”,则属于语言“L”的字符串是正则的,您可以根据需要多次重复 y
,整个字符串仍属于 L
。
抽水引理的一个结果是不能有 a^Nb^Mc^N
形式的常规字符串,即两个具有相同长度的子字符串被另一个字符串分隔。以任何方式将此类字符串拆分为 x
、y
和 z
,如果不获取具有不同数量“a”和“c”的字符串,则无法“泵送”y
,从而留下原始语言.例如,正则表达式中的括号就是这种情况。
尽管完全可以使用 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
,如果正则表达式无效,它将返回 false。不要忘记使用 @
来隐藏错误消息:
@preg_match($regexToTest, '');
如果正则表达式是 //,将返回 1。
如果正则表达式没问题,将返回 0。
否则将返回 false。
以下由 Paul McGuire 编写的示例(最初来自 pyparsing wiki,但现在为 available only through the Wayback Machine)给出了解析 some 正则表达式的语法,以返回匹配字符串集。因此,它拒绝那些包含无限重复项的 re's,如“+”和“*”。但它应该让您了解如何构建一个可以处理 re 的解析器。
#
# 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()
在 Javascript 中:
SyntaxError
当一个无效的正则表达式被传递给评估时抛出。
// VALID ONE
> /yes[^]*day/
Out: /yes[^]*day/
// INVALID ONE
> /yes[^*day/
Out: VM227:1 Uncaught SyntaxError: Invalid regular expression: missing /
这是检查正则表达式字符串是否有效的函数:
第 1 步:正则表达式解析器
var RegexParser = function(input) {
// Parse input
var m = input.match(/(\/?)(.+)\1([a-z]*)/i);
// Invalid flags
if (m[3] && !/^(?!.*?(.).*?\1)[gmixXsuUAJ]+$/.test(m[3])) {
return RegExp(input);
}
// Create the regular expression
return new RegExp(m[2], m[3]);
};
第 2 步:使用解析器
var RegexString = "/yes.*day/"
var isRegexValid = input => {
try {
const regex = RegexParser(input);
}
catch(error) {
if(error.name === "SyntaxError")
{
return false;
}
else
{
throw error;
}
}
return true;
}
.{,1}
不匹配。更改为^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$
个匹配项。将\d+
更改为\d*