programing tip

유효한 정규식을 감지하는 정규식이 있습니까?

itbloger 2020. 9. 28. 08:23
반응형

유효한 정규식을 감지하는 정규식이 있습니까?


다른 정규식으로 유효한 정규식을 감지 할 수 있습니까? 그렇다면 아래에 예제 코드를 제공하십시오.


/
^                                             # 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 명령에서 쉘 스크립트와 함께 사용하면 오류가 표시됩니다. grep : {}의 유효하지 않은 컨텐츠. 도와 주실 수 있나요? 정규 표현식이 포함 된 모든 파일을 찾기 위해 코드베이스를 grep 할 수있는 스크립트를 만들고 있습니다.

이 패턴은 재귀 정규식이라는 확장을 이용합니다. 이것은 regex의 POSIX 풍미에서 지원되지 않습니다. -P 스위치를 사용하여 PCRE 정규식 특징을 활성화 할 수 있습니다.

정규식 자체는 "정규 언어가 아니므로 정규식으로 구문 분석 할 수 없습니다 ..."

이것은 고전적인 정규 표현식에 해당됩니다. 일부 현대 구현에서는 재귀를 허용하므로이 작업에는 다소 장황하지만 컨텍스트가없는 언어로 만듭니다.

나는 당신이 일치하는 곳을 봅니다 []()/\. 및 기타 특수 정규식 문자. 비 특수 문자는 어디에서 허용합니까? 일치하는 것 같지만 일치 ^(?:[\.]+)$하지 않습니다 ^abcdefg$. 그것은 유효한 정규식입니다.

[^?+*{}()[\]\\|]다른 구성의 일부가 아닌 단일 문자와 일치합니다. (이 두 문자 포함 a- z), 특정 특수 문자 ( ^, $, .).


있을 것 같지 않게.

try..catch당신의 언어가 제공 하는 어떤 것으로 평가하십시오 .


정규식에 대해 엄격하게 말하고 실제로 문맥 자유 문법 인 일부 정규식 구현을 포함하지 않는 경우 아니요.

정규식에는 모든 정규식과 일치하는 정규식을 작성할 수없는 한 가지 제한이 있습니다. 쌍을 이루는 중괄호와 같은 구현은 일치시킬 수 없습니다. 정규식은 이러한 구조를 많이 사용하므로 []를 예로 들어 보겠습니다. [일치하는 항목이 있어야 함]이있을 때마다. 정규식 "[. *]"에 대해 충분히 간단합니다.

정규식을 불가능하게 만드는 것은 중첩 될 수 있다는 것입니다. 중첩 괄호와 일치하는 정규식을 어떻게 작성할 수 있습니까? 대답은 무한히 긴 정규식 없이는 할 수 없다는 것입니다. 무차별 대괄호를 통해 임의의 수의 중첩 괄호를 일치시킬 수 있지만 임의의 긴 중첩 괄호 집합은 일치시킬 수 없습니다.

이 기능을 종종 계수라고합니다 (중첩의 깊이를 계산합니다). 정의에 따른 정규식에는 계산 기능이 없습니다.

편집 : 이것에 대한 블로그 게시물 작성 : 정규식 제한


좋은 질문. 진정한 정규 언어는 임의적으로 깊이 중첩 된 잘 형성된 괄호를 결정할 수 없습니다. 즉, 알파벳에 '('및 ')'가 포함되어있는 경우 목표는 이러한 문자열에 올바른 형식의 일치하는 괄호가 있는지 결정하는 것입니다. 정규 표현식에 필요한 요구 사항이므로 대답은 '아니요'입니다.

그러나 요구 사항을 풀고 재귀를 추가하면 아마도 할 수 있습니다. 그 이유는 재귀가이 스택에 푸시하여 현재 중첩 깊이를 '계산'할 수있는 '스택'역할을 할 수 있기 때문입니다.

Russ Cox는 정규식 엔진 구현에 대한 멋진 논문을 작성했습니다. 정규식 일치는 간단하고 빠를 수 있습니다.


아니오, 표준 정규식을 사용하는 경우.

그 이유는 일반 언어에 대한 펌핑 기본형충족시킬 수 없기 때문입니다 . 펌핑 기본형은 숫자 N이있는 경우 언어 L에 속하는 문자열이 규칙적이라고 명시합니다. 따라서 문자열을 3 개의 하위 문자열 xyz로 나눈 후 | x |> = 1 && | xy | <= N을 반복 할 수 있습니다. 원하는만큼 여러 번 문자열 전체가 L에 속합니다.

펌핑 기본형의 결과는 형식의 일반 문자열 a^Nb^Mc^N, 즉 다른 문자열로 구분 된 동일한 길이를 갖는 두 개의 하위 문자열을 가질 수 없다는 것 입니다. 어떤 식 으로든 이러한 문자열을 xy 및 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; }
}

정규식이 유효하지 않은 경우 false를 반환하는 preg_match에 정규식을 제출할 수 있습니다. 오류 메시지를 억제하려면 '@'를 사용하는 것을 잊지 마십시오.

@preg_match($regexToTest, '');
  • 정규식이 '//'이면 1을 반환합니다.
  • 정규식이 정상이면 0을 반환합니다.
  • 그렇지 않으면 거짓을 반환합니다.

원래 pyparsing wiki에서 왔지만 이제는 Wayback Machine을 통해서만 사용할 수 있는 Paul McGuire의 다음 예제 는 일치하는 문자열 집합을 반환하기 위해 일부 정규식 을 구문 분석하는 문법을 제공합니다 . 따라서 '+'및 '*'와 같은 무제한 반복 용어를 포함하는 re는 거부합니다. 그러나 re 's를 처리하는 파서를 구성하는 방법에 대한 아이디어를 제공해야합니다.

# 
# 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()

참고 URL : https://stackoverflow.com/questions/172303/is-there-a-regular-expression-to-detect-a-valid-regular-expression

반응형