Có thể phát hiện một biểu thức chính quy hợp lệ với một biểu thức chính quy khác không? Nếu vậy xin vui lòng cho mã ví dụ dưới đây.
Có thể phát hiện một biểu thức chính quy hợp lệ với một biểu thức chính quy khác không? Nếu vậy xin vui lòng cho mã ví dụ dưới đây.
Câu trả lời:
/
^ # 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
/
Đây là một regex đệ quy và không được hỗ trợ bởi nhiều công cụ regex. Những người dựa trên PCRE nên hỗ trợ nó.
Không có khoảng trắng và bình luận:
/^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*)$/
.NET không hỗ trợ đệ quy trực tiếp. (The (?1)
and (?R)
constructs.) Đệ quy sẽ phải được chuyển đổi thành các nhóm cân bằng:
^ # 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.
Nén:
^(?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>|\?<[^\W\d]\w*>|\?'[^\W\d]\w*')?(?<N>)|\)(?<-N>))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*$(?(N)(?!))
Từ các ý kiến:
Điều này sẽ xác nhận thay thế và bản dịch?
Nó sẽ xác nhận chỉ là phần regex của sự thay thế và bản dịch. s/<this part>/.../
Về mặt lý thuyết không thể phù hợp với tất cả các ngữ pháp regex hợp lệ với regex.
Có thể nếu công cụ regex hỗ trợ đệ quy, chẳng hạn như PCRE, nhưng điều đó thực sự không thể được gọi là biểu thức chính quy nữa.
Thật vậy, một "biểu thức chính quy đệ quy" không phải là biểu thức chính quy. Nhưng đây là một phần mở rộng thường được chấp nhận cho các công cụ regex ... Trớ trêu thay, regex mở rộng này không phù hợp với các biểu thức mở rộng.
"Về lý thuyết, lý thuyết và thực hành là như nhau. Trong thực tế, chúng không như vậy." Hầu hết mọi người biết biểu thức chính quy đều biết rằng biểu thức chính quy không hỗ trợ đệ quy. Nhưng PCRE và hầu hết các triển khai khác hỗ trợ nhiều hơn các biểu thức thông thường cơ bản.
sử dụng điều này với shell script trong lệnh grep, nó cho tôi thấy một số lỗi .. grep: Nội dung không hợp lệ của {}. Tôi đang tạo một tập lệnh có thể grep một cơ sở mã để tìm tất cả các tệp có chứa các biểu thức thông thường
Mẫu này khai thác một phần mở rộng được gọi là biểu thức chính quy đệ quy. Điều này không được hỗ trợ bởi hương vị POSIX của regex. Bạn có thể thử với công tắc -P, để bật hương vị regex PCRE.
Bản thân Regex "không phải là ngôn ngữ thông thường và do đó không thể được phân tích cú pháp bằng biểu thức chính quy ..."
Điều này đúng với các biểu thức chính quy cổ điển. Một số triển khai hiện đại cho phép đệ quy, làm cho nó thành ngôn ngữ Ngữ cảnh miễn phí, mặc dù nó hơi dài dòng cho nhiệm vụ này.
Tôi thấy nơi bạn phù hợp
[]()/\
. và các nhân vật regex đặc biệt khác. Bạn đang cho phép các nhân vật không đặc biệt ở đâu? Có vẻ như điều này sẽ phù hợp^(?:[\.]+)$
, nhưng không^abcdefg$
. Đó là một regex hợp lệ.
[^?+*{}()[\]\\|]
sẽ khớp với bất kỳ ký tự đơn nào, không phải là một phần của bất kỳ cấu trúc nào khác. Điều này bao gồm cả nghĩa đen ( a
- z
), và một số ký tự đặc biệt ( ^
, $
, .
).
.{,1}
là vô song. Thay đổi để ^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$
phù hợp. Thay đổi \d+
để\d*
[a-b-c]
.
Không, nếu bạn nghiêm túc nói về các biểu thức chính quy và không bao gồm một số triển khai biểu thức chính quy thực sự là ngữ pháp tự do ngữ cảnh.
Có một hạn chế của các biểu thức thông thường khiến cho không thể viết một biểu thức chính phù hợp với tất cả và chỉ các biểu thức chính. Bạn không thể kết hợp các triển khai như niềng răng được ghép nối. Regexes sử dụng nhiều cấu trúc như vậy, hãy lấy []
một ví dụ. Bất cứ khi nào có một [
phải có một kết hợp ]
, đủ đơn giản cho một regex "\[.*\]"
.
Điều khiến cho regexes không thể là chúng có thể được lồng vào nhau. Làm thế nào bạn có thể viết một regex phù hợp với dấu ngoặc lồng? Câu trả lời là bạn không thể không có regex dài vô tận. Bạn có thể khớp bất kỳ số ngoặc đơn lồng nhau nào thông qua lực lượng vũ phu nhưng bạn không bao giờ có thể khớp với một bộ dấu ngoặc lồng dài tùy ý.
Khả năng này thường được gọi là đếm, bởi vì bạn đang đếm độ sâu của lồng. Một regex theo định nghĩa không có khả năng đếm.
Cuối cùng tôi đã viết " Hạn chế biểu hiện thường xuyên " về điều này.
Câu hỏi hay.
Các ngôn ngữ thông thường thực sự không thể quyết định dấu ngoặc đơn được tổ chức tốt. Nếu bảng chữ cái của bạn chứa '('
và ')'
mục tiêu là quyết định xem một chuỗi trong số này có dấu ngoặc đơn phù hợp được hình thành tốt hay không. Vì đây là một yêu cầu cần thiết cho các biểu thức chính quy, câu trả lời là không.
Tuy nhiên, nếu bạn nới lỏng yêu cầu và thêm đệ quy, bạn có thể làm điều đó. Lý do là đệ quy có thể hoạt động như một ngăn xếp cho phép bạn "đếm" độ sâu làm tổ hiện tại bằng cách đẩy lên ngăn xếp này.
Russ Cox đã viết " Kết hợp biểu thức chính quy có thể đơn giản và nhanh chóng ", đây là một chuyên luận tuyệt vời về triển khai động cơ regex.
Không, nếu bạn sử dụng các biểu thức chính quy tiêu chuẩn.
Lý do là bạn không thể đáp ứng bổ đề bơm cho các ngôn ngữ thông thường. Các trạng thái bơm Bổ đề mà một chuỗi thuộc ngôn ngữ "L" là bình thường nếu có tồn tại một số "N" như vậy mà, sau khi chia chuỗi thành ba chuỗi con x
, y
, z
, như vậy |x|>=1 && |xy|<=N
, bạn có thể lặp lại y
nhiều lần như bạn muốn và toàn bộ chuỗi sẽ vẫn thuộc về L
.
Một hậu quả của bổ đề bơm là bạn không thể có các chuỗi thông thường ở dạng a^Nb^Mc^N
, nghĩa là hai chuỗi con có cùng độ dài cách nhau bởi một chuỗi khác. Trong bất kỳ cách nào bạn chia chuỗi như vậy trong x
, y
và z
, bạn không thể "bơm" y
mà không có một chuỗi với một số khác nhau của "a" và "c", do đó để lại ngôn ngữ gốc. Đó là trường hợp, ví dụ, với dấu ngoặc đơn trong các biểu thức thông thường.
Mặc dù hoàn toàn có thể sử dụng một biểu thức đệ quy như MizardX đã đăng, nhưng đối với những thứ này, trình phân tích cú pháp sẽ hữu ích hơn nhiều. Regexes ban đầu được dự định sẽ được sử dụng với các ngôn ngữ thông thường, được đệ quy hoặc có các nhóm cân bằng chỉ là một bản vá.
Ngôn ngữ xác định regexes hợp lệ thực sự là một ngữ pháp không ngữ cảnh và bạn nên sử dụng một trình phân tích cú pháp thích hợp để xử lý nó. Dưới đây là một ví dụ cho một dự án đại học để phân tích các biểu thức chính đơn giản (không có hầu hết các cấu trúc). Nó sử dụng JavaCC. Và vâng, các bình luận bằng tiếng Tây Ban Nha, mặc dù tên phương thức khá tự giải thích.
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; }
}
Bạn có thể gửi regex tới preg_match
đó sẽ trả về false nếu regex không hợp lệ. Đừng quên sử dụng @
để chặn thông báo lỗi:
@preg_match($regexToTest, '');
//
.Ví dụ sau đây của Paul McGuire, ban đầu từ wiki pyparsing, nhưng hiện chỉ có sẵn thông qua Wayback Machine , đưa ra một ngữ pháp để phân tích một số regex, với mục đích trả về tập hợp các chuỗi phù hợp. Như vậy, nó từ chối những thứ đó bao gồm các điều khoản lặp lại không giới hạn, như '+' và '*'. Nhưng nó sẽ cho bạn một ý tưởng về cách cấu trúc một trình phân tích cú pháp sẽ xử lý lại.
#
# 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()