เป็นไปได้หรือไม่ที่จะตรวจพบนิพจน์ปกติที่ถูกต้องกับนิพจน์ทั่วไปอื่น ถ้าเป็นเช่นนั้นโปรดให้รหัสตัวอย่างด้านล่าง
เป็นไปได้หรือไม่ที่จะตรวจพบนิพจน์ปกติที่ถูกต้องกับนิพจน์ทั่วไปอื่น ถ้าเป็นเช่นนั้นโปรดให้รหัสตัวอย่างด้านล่าง
คำตอบ:
/
^ # 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
/
นี่คือ regex แบบเรียกซ้ำและไม่ได้รับการสนับสนุนจากเอนจิน regex จำนวนมาก คนที่ใช้ 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)(?!))
จากความคิดเห็นที่:
สิ่งนี้จะตรวจสอบการแทนที่และการแปลหรือไม่
มันจะตรวจสอบเพียงส่วน regex ของการทดแทนและการแปล s/<this part>/.../
เป็นไปไม่ได้ในทางทฤษฎีที่จะจับคู่ไวยากรณ์ที่ถูกต้องทั้งหมดกับ regex
เป็นไปได้ถ้าเอ็นจิน regex รองรับการเรียกซ้ำเช่น PCRE แต่จะไม่สามารถเรียกนิพจน์ทั่วไปได้อีกต่อไป
แท้จริงแล้ว "นิพจน์ทั่วไปแบบเรียกซ้ำ" ไม่ใช่นิพจน์ทั่วไป แต่นี่เป็นส่วนขยายที่ได้รับการยอมรับบ่อยๆสำหรับเครื่องมือ regex ... โดยปกติแล้ว regex ที่ขยายนี้ไม่ตรงกับ regex ที่ขยายเพิ่ม
“ ในทางทฤษฎีทฤษฏีและการปฏิบัติเหมือนกันในทางปฏิบัติพวกเขาไม่ได้” เกือบทุกคนที่รู้ว่านิพจน์ทั่วไปรู้ว่านิพจน์ทั่วไปไม่รองรับการเรียกซ้ำ แต่ PCRE และการใช้งานอื่น ๆ ส่วนใหญ่รองรับมากกว่าการแสดงออกปกติพื้นฐาน
การใช้สิ่งนี้กับเชลล์สคริปต์ในคำสั่ง grep มันแสดงให้ฉันเห็นข้อผิดพลาดบางอย่าง .. grep: เนื้อหาไม่ถูกต้องของ {} ฉันกำลังสร้างสคริปต์ที่สามารถ grep ฐานรหัสเพื่อค้นหาไฟล์ทั้งหมดที่มีการแสดงออกปกติ
รูปแบบนี้ใช้ประโยชน์จากส่วนขยายที่เรียกว่านิพจน์ทั่วไปแบบเรียกซ้ำ สิ่งนี้ไม่สนับสนุนโดย POSIX รสชาติของ regex คุณสามารถลองด้วยสวิตช์ -P เพื่อเปิดใช้งานรสชาติ PCRE regex
Regex เอง "ไม่ใช่ภาษาปกติดังนั้นจึงไม่สามารถแยกวิเคราะห์ด้วยนิพจน์ทั่วไป ... "
นี่เป็นความจริงสำหรับนิพจน์ปกติแบบคลาสสิก การใช้งานที่ทันสมัยบางอย่างอนุญาตให้เรียกซ้ำซึ่งทำให้เป็นภาษาบริบทฟรีแม้ว่าจะค่อนข้างละเอียดสำหรับงานนี้
[]()/\
ผมเห็นที่คุณกำลังจับคู่ และอักขระพิเศษ regex อื่น ๆ คุณอนุญาตตัวละครที่ไม่ใช่พิเศษอยู่ที่ไหน ดูเหมือนว่านี้จะตรงแต่ไม่^(?:[\.]+)$
^abcdefg$
นั่นคือ regex ที่ถูกต้อง
[^?+*{}()[\]\\|]
จะจับคู่อักขระเดี่ยวใด ๆ ไม่ใช่ส่วนหนึ่งของโครงสร้างอื่น นี้รวมทั้งตัวอักษร ( a
- z
) และอักขระพิเศษบางอย่าง ( ^
, $
, .
)
.{,1}
ไม่มีที่เปรียบ เปลี่ยนเป็นการ^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$
แข่งขัน เปลี่ยน\d+
เป็น\d*
[a-b-c]
ERE:
ไม่แน่
ประเมินเป็นtry..catch
ภาษาของคุณเอง
ไม่ถ้าคุณกำลังพูดอย่างเคร่งครัดเกี่ยวกับการแสดงออกปกติและไม่รวมถึงการใช้งานการแสดงออกปกติบางอย่างที่เป็นจริงไวยากรณ์บริบทฟรี
มีข้อ จำกัด อย่างหนึ่งของนิพจน์ทั่วไปซึ่งทำให้ไม่สามารถเขียน regex ที่ตรงกับ regexes ทั้งหมดและเฉพาะได้ คุณไม่สามารถจับคู่การติดตั้งใช้งานเช่นเครื่องมือจัดฟันที่จับคู่ไว้ Regexes ใช้โครงสร้างดังกล่าวมากมายลองมา[]
เป็นตัวอย่าง เมื่อใดก็ตามที่มีการ[
จะต้องมีการจับคู่ซึ่งเพียงพอที่ง่ายสำหรับนิพจน์ทั่วไป]
"\[.*\]"
สิ่งที่ทำให้เป็นไปไม่ได้สำหรับ regexes ก็คือพวกมันสามารถซ้อนกันได้ คุณจะเขียน regex ที่ตรงกับเครื่องหมายวงเล็บซ้อนได้อย่างไร คำตอบคือคุณไม่สามารถทำได้โดยไม่ต้อง regex ยาวอย่างไม่ จำกัด คุณสามารถจับคู่วงเล็บที่ซ้อนกันจำนวนเท่าใดก็ได้ผ่านการใช้กำลังดุร้าย แต่คุณไม่สามารถจับคู่ชุดซ้อนซ้อนแบบยาวโดยพลการได้
ความสามารถนี้มักเรียกว่าการนับเนื่องจากคุณกำลังนับความลึกของการทำรัง regex ตามคำนิยามไม่มีความสามารถในการนับ
ฉันสิ้นสุดการเขียน " ข้อ จำกัด นิพจน์ปกติ " เกี่ยวกับเรื่องนี้
คำถามที่ดี.
ภาษาปกติที่แท้จริงไม่สามารถตัดสินวงเล็บที่มีรูปแบบที่ดีได้โดยพลการ หากตัวอักษรของคุณมี'('
และ')'
เป้าหมายคือการตัดสินใจว่าสตริงของสิ่งเหล่านี้มีวงเล็บที่ตรงกันหรือไม่ เนื่องจากนี่เป็นข้อกำหนดที่จำเป็นสำหรับนิพจน์ทั่วไปคำตอบคือไม่
อย่างไรก็ตามหากคุณคลายความต้องการและเพิ่มการสอบถามซ้ำคุณอาจทำได้ เหตุผลคือการเรียกซ้ำสามารถทำหน้าที่เป็นกองซ้อนเพื่อให้คุณ "นับ" ความลึกการซ้อนปัจจุบันโดยกดลงบนสแต็กนี้
Russ Cox เขียนว่า " การจับคู่นิพจน์ทั่วไปทำได้ง่ายและรวดเร็ว " ซึ่งเป็นบทความที่ยอดเยี่ยมเกี่ยวกับการใช้เอ็นจิ้นของ regex
ไม่ถ้าคุณใช้นิพจน์ปกติมาตรฐาน
เหตุผลก็คือคุณไม่สามารถตอบสนองบทแทรกสำหรับภาษาปกติได้ สูบน้ำแทรกระบุว่าเป็นสายที่อยู่ในภาษา "L" คือปกติถ้ามีจำนวน "N" ดังกล่าวว่าหลังจากที่การแบ่งสายออกเป็นสามสตริงx
, y
, z
เช่นว่า|x|>=1 && |xy|<=N
คุณสามารถทำซ้ำy
หลายครั้งตามที่คุณต้องการและ L
สตริงทั้งหมดจะยังคงอยู่
ผลของการแทรกบทแทรกคือคุณไม่สามารถมีสตริงปกติในรูปแบบa^Nb^Mc^N
ได้นั่นคือสองสตริงย่อยที่มีความยาวเท่ากันคั่นด้วยสตริงอื่น ไม่ว่าคุณจะแยกสตริงดังกล่าวเป็นx
อย่างไรy
และz
คุณไม่สามารถ "ปั๊ม" y
โดยไม่ได้รับสตริงที่มีจำนวน "a" และ "c" ที่แตกต่างกันดังนั้นจึงเป็นการออกจากภาษาต้นฉบับ ตัวอย่างเช่นกรณีที่มีวงเล็บเป็นนิพจน์ทั่วไป
แม้ว่ามันจะเป็นไปได้อย่างสมบูรณ์ที่จะใช้ regex แบบเรียกซ้ำตามที่ MizardX โพสต์ไว้ แต่สำหรับสิ่งนี้มันมีประโยชน์มากในการแยกวิเคราะห์ Regexes เดิมทีตั้งใจจะใช้กับภาษาปกติ, การเรียกซ้ำหรือการมีกลุ่มที่สมดุลเป็นเพียงการปะแก้
ภาษาที่กำหนด regexes ที่ถูกต้องเป็นจริงไวยากรณ์บริบทและคุณควรใช้ parser ที่เหมาะสมสำหรับการจัดการ นี่คือตัวอย่างสำหรับโครงการมหาวิทยาลัยสำหรับการแยกวิเคราะห์ regexes แบบง่าย ๆ มันใช้ 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; }
}
คุณสามารถส่ง regex preg_match
ที่จะคืนค่าเท็จถ้า regex ไม่ถูกต้อง อย่าลืมใช้ข้อความ@
เพื่อระงับข้อความแสดงข้อผิดพลาด:
@preg_match($regexToTest, '');
//
คือตัวอย่างต่อไปนี้โดย Paul McGuire มีพื้นเพมาจาก pyparsing wiki แต่ขณะนี้มีให้บริการผ่านเครื่อง Waybackเท่านั้นให้ไวยากรณ์สำหรับการแยกวิเคราะห์regexes บางส่วนเพื่อวัตถุประสงค์ในการส่งคืนชุดสตริงที่ตรงกัน ด้วยเหตุนี้มันจึงปฏิเสธผู้ที่มีเงื่อนไขการทำซ้ำที่ไม่ได้ จำกัด เช่น '+' และ '*' แต่ควรให้แนวคิดเกี่ยวกับวิธีจัดโครงสร้างตัวแยกวิเคราะห์ที่จะประมวลผล
#
# 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()