是否有一个正则表达式来检测一个有效的正则表达式?

是否可以使用另一个正则表达式检测有效的正则表达式?如果是,请在下面给出示例代码。

248215 次浏览

不太可能。

try..catch或你的语言提供的任何东西来评估它。

/^                                             # 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>/.../

理论上不可能将所有有效的正则表达式语法与正则表达式匹配。

如果regex引擎支持递归,例如PCRE,这是可能的,但这不能再称为正则表达式了。

事实上,“递归正则表达式”不是正则表达式。但这是一个经常被接受的正则表达式引擎的扩展……具有讽刺意味的是,这个扩展的正则表达式不匹配扩展的正则表达式。

“理论上,理论和实践是一样的。在实践中,它们不是。”几乎所有了解正则表达式的人都知道正则表达式不支持递归。但PCRE和大多数其他实现支持的远不止基本正则表达式。

在grep命令中使用外壳脚本,它显示了一些错误… grep:{}的无效内容。我正在制作一个脚本,可以grep代码库来查找所有包含正则表达式的文件

此模式利用称为递归正则表达式的扩展。这不受正则表达式的POSIX风格的支持。您可以尝试使用-P开关来启用PCRE正则表达式风格。

正则表达式本身“不是正则语言,因此不能被正则表达式解析……”

经典正则表达式就是如此。一些现代实现允许递归,这使其成为一种无上下文语言,尽管它对这项任务有点冗长。

我看到您在哪里匹配[]()/\。和其他特殊正则表达式字符。您在哪里允许非特殊字符?看起来这将匹配^(?:[\.]+)$,但不匹配^abcdefg$。这是一个有效的正则表达式。

[^?+*{}()[\]\\|]将匹配任何单个字符,而不是任何其他构造的一部分。这包括文字(a-z)和某些特殊字符(^$.)。

问得好。

真正的正则语言不能任意决定深度嵌套的格式良好的括号。如果您的字母表包含'('')',则目标是确定其中的字符串是否具有格式良好的匹配括号。由于这是正则表达式的必要要求,答案是否定的。

但是,如果您放松需求并添加递归,您可能可以做到这一点。原因是递归可以充当堆栈,让您通过推送到此堆栈来“计数”当前嵌套深度。

Russ Cox写了“正则表达式匹配可以简单而快速”,这是一篇关于regex引擎实现的精彩论文。

不,如果您严格地说正则表达式并且不包括一些实际上是上下文无关语法的正则表达式实现。

正则表达式有一个限制,这使得不可能编写匹配所有且仅匹配正则表达式的正则表达式。您不能匹配诸如配对的大括号之类的实现。正则表达式使用许多这样的结构,让我们以[]为例。每当有[时,必须有匹配的],这对于正则表达式"\[.*\]"来说足够简单。

正则表达式之所以不可能,是因为它们可以嵌套。你怎么能写出匹配嵌套括号的正则表达式呢?答案是你不能没有无限长的正则表达式。你可以通过蛮力匹配任意数量的嵌套括号,但你永远不能匹配任意长的嵌套括号集。

此功能通常称为计数,因为您正在计算嵌套的深度。根据定义,正则表达式没有计数能力。


为此,我写了“正则表达式限制”。

尽管使用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 | ERIRegularExpression Alternation() :{IRegularExpression r1 = null, r2 = null;}{r1=Concatenation() ( "|" r2=Alternation() )?{if (r2 == null) {return r1;} else {return createAlternation(r1,r2);}}}
// Matchea concatenaciones: ER.ERIRegularExpression 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, LambdaIRegularExpression 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 valorString Terminal() :{Token t;}{( t=<DIGITO> | t=<MINUSCULA> ) { return t.image; }}

下面Paul McGuire的例子,最初来自pyparsing wiki,但现在只能通过Wayback Machine获得,给出了解析一些正则表达式的语法,目的是返回匹配的字符串集。因此,它拒绝那些包含无限重复项的re,如'+'和'*'。但它应该让你了解如何构建一个处理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 orderseen = 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 sreturn genChars
class OptionalEmitter(object):def __init__(self,expr):self.expr = exprdef makeGenerator(self):def optionalGen():yield ""for s in self.expr.makeGenerator()():yield sreturn optionalGen
class DotEmitter(object):def makeGenerator(self):def dotGen():for c in printables:yield creturn 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 selse:for s in elist[0].makeGenerator()():for s2 in recurseList(elist[1:]):yield s + s2if self.exprs:for s in recurseList(self.exprs):yield sreturn groupGen
class AlternativeEmitter(object):def __init__(self,exprs):self.exprs = exprsdef makeGenerator(self):def altGen():for e in self.exprs:for s in e.makeGenerator()():yield sreturn altGen
class LiteralEmitter(object):def __init__(self,lit):self.lit = litdef __str__(self):return "Lit:"+self.litdef __repr__(self):return "Lit:"+self.litdef makeGenerator(self):def litGen():yield self.litreturn 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 - mincountif 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 += treturn 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 = Nonedef parser():global _parserif _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 = 0for s in gen:i += 1return i
def invert(regex):"""Call this routine as a generator to return all the strings thatmatch 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}YX[A-C]{3}\(X\dfoobar\d\dfoobar{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: continueprint '-'*50print ttry:print count(invert(t))for s in invert(t):print sexcept ParseFatalException,pfe:print pfe.msgprintcontinueprint
if __name__ == "__main__":main()

您可以将正则表达式提交给preg_match,如果正则表达式无效,它将返回false。不要忘记使用@来抑制错误消息:

@preg_match($regexToTest, '');
  • 如果正则表达式为//,则返回1。
  • 如果正则表达式正常,将返回0。
  • 否则将返回false。

不,如果您使用标准正则表达式。

原因是你不能满足正则语言的泵浦引理。泵浦引理指出,如果存在一个数字“N”,则属于语言“L”的字符串是正则的,这样,在将字符串分成三个子字符串xyz之后,这样|x|>=1 && |xy|<=N,你可以随心所欲地重复y多次,整个字符串仍然属于L

抽运引理的一个结果是,你不能有a^Nb^Mc^N形式的正则字符串,也就是说,两个长度相同的子字符串被另一个字符串分隔。无论你以何种方式在xyz中拆分这样的字符串,你都不能在没有获得具有不同数量“a”和“c”的字符串的情况下“抽”y,从而离开原始语言。例如,正则表达式中的括号就是这种情况。

在Javascript中:

SyntaxError

当传递无效的正则表达式以进行评估时引发。

// VALID ONE> /yes[^]*day/Out: /yes[^]*day/
// INVALID ONE> /yes[^*day/Out: VM227:1 Uncaught SyntaxError: Invalid regular expression: missing /

以下是检查regex字符串是否有效的函数:

步骤1:正则表达式解析器

var RegexParser = function(input) {
// Parse inputvar m = input.match(/(\/?)(.+)\1([a-z]*)/i);
// Invalid flagsif (m[3] && !/^(?!.*?(.).*?\1)[gmixXsuUAJ]+$/.test(m[3])) {return RegExp(input);}
// Create the regular expressionreturn 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;}