È possibile rilevare un'espressione regolare valida con un'altra espressione regolare? In tal caso, fornire il seguente esempio di codice.
È possibile rilevare un'espressione regolare valida con un'altra espressione regolare? In tal caso, fornire il seguente esempio di codice.
Risposte:
/
^ # 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
/
Questa è una regex ricorsiva e non è supportata da molti motori regex. Quelli basati su PCRE dovrebbero supportarlo.
Senza spazi bianchi e commenti:
/^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*)$/
.NET non supporta direttamente la ricorsione. (The (?1)
e (?R)
costrutti.) La ricorsione dovrebbe essere convertita in conteggio di gruppi bilanciati:
^ # 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.
Compatto:
^(?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>|\?<[^\W\d]\w*>|\?'[^\W\d]\w*')?(?<N>)|\)(?<-N>))(?:(?:[?+*]|\{\d+(?:,\d*)?\})[?+]?)?|\|)*$(?(N)(?!))
Dai commenti:
Convalideranno sostituzioni e traduzioni?
Convaliderà solo la parte regex di sostituzioni e traduzioni. s/<this part>/.../
Non è teoricamente possibile abbinare tutte le grammatiche regex valide con una regex.
È possibile se il motore regex supporta la ricorsione, come PCRE, ma ciò non può più essere chiamato espressioni regolari.
In effetti, una "espressione regolare ricorsiva" non è un'espressione regolare. Ma questa è un'estensione spesso accettata per i motori regex ... Ironia della sorte, questa regex estesa non corrisponde alle regex estese.
"In teoria, teoria e pratica sono le stesse. In pratica, non lo sono." Quasi tutti coloro che conoscono le espressioni regolari sanno che le espressioni regolari non supportano la ricorsione. Ma PCRE e la maggior parte delle altre implementazioni supportano molto più delle espressioni regolari di base.
usando questo con lo script della shell nel comando grep, mi mostra qualche errore. grep: contenuto non valido di {}. Sto realizzando uno script che potrebbe grep una base di codice per trovare tutti i file che contengono espressioni regolari
Questo modello sfrutta un'estensione chiamata espressioni regolari ricorsive. Questo non è supportato dal sapore POSIX di regex. Puoi provare con l'opzione -P, per abilitare il sapore regex di PCRE.
Lo stesso Regex "non è un linguaggio regolare e quindi non può essere analizzato dall'espressione regolare ..."
Questo è vero per le espressioni regolari classiche. Alcune implementazioni moderne consentono la ricorsione, che lo rende un linguaggio senza contesto, sebbene sia in qualche modo dettagliato per questo compito.
Vedo dove ti stai abbinando
[]()/\
. e altri personaggi speciali regex. Dove stai permettendo i personaggi non speciali? Sembra che questo corrisponderà^(?:[\.]+)$
, ma non^abcdefg$
. Questa è una regex valida.
[^?+*{}()[\]\\|]
corrisponderà a qualsiasi singolo personaggio, non parte di nessuno degli altri costrutti. Questo include sia letterale ( a
- z
), e alcuni caratteri speciali ( ^
, $
, .
).
.{,1}
non ha eguali. Passa alle ^((?:(?:[^?+*{}()[\]\\|]+|\\.|\[(?:\^?\\.|\^[^\\]|[^\\^])(?:[^\]\\]+|\\.)*\]|\((?:\?[:=!]|\?<[=!]|\?>)?(?1)??\)|\(\?(?:R|[+-]?\d+)\))(?:(?:[?+*]|\{\d*(?:,\d*)?\})[?+]?)?|\|)*)$
partite. Cambiare \d+
a\d*
[a-b-c]
.
No, se parli rigorosamente di espressioni regolari e non includi alcune implementazioni di espressioni regolari che sono in realtà grammatiche libere dal contesto.
C'è una limitazione delle espressioni regolari che rende impossibile scrivere una regex che corrisponda a tutte e solo le regex. Non è possibile abbinare implementazioni come parentesi graffe che sono accoppiate. I regex usano molti di questi costrutti, prendiamo []
come esempio. Ogni volta che c'è un, [
ci deve essere una corrispondenza ]
, che è abbastanza semplice per una regex"\[.*\]"
.
Ciò che rende impossibile la regex è che possono essere nidificati. Come puoi scrivere una regex che corrisponde alle parentesi nidificate? La risposta è che non puoi fare a meno di una regex infinitamente lunga. È possibile abbinare qualsiasi numero di parentesi nidificate tramite la forza bruta, ma non è mai possibile abbinare un set arbitrariamente lungo di parentesi nidificate.
Questa funzionalità viene spesso definita come conteggio, poiché si sta contando la profondità della nidificazione. Una regex per definizione non ha la capacità di contare.
Ho finito per scrivere " Limitazioni di espressioni regolari " su questo.
Buona domanda.
Le vere lingue regolari non possono decidere tra parentesi ben formate nidificate in modo arbitrario. Se il tuo alfabeto contiene '('
e')'
l'obiettivo è decidere se una stringa di questi ha parentesi corrispondenti ben formate. Poiché questo è un requisito necessario per le espressioni regolari, la risposta è no.
Tuttavia, se allenti il requisito e aggiungi ricorsione, probabilmente puoi farlo. Il motivo è che la ricorsione può agire come uno stack che consente di "contare" l'attuale profondità di annidamento spingendo su questo stack.
Russ Cox ha scritto " La corrispondenza delle espressioni regolari può essere semplice e veloce ", che è un meraviglioso trattato sull'implementazione del motore regex.
No, se usi espressioni regolari standard.
Il motivo è che non puoi soddisfare il lemma del pompaggio per le lingue normali. Gli stati di pompaggio lemma che una stringa appartenente al linguaggio "L" è regolare se esiste un numero "N" in modo tale che, dopo aver diviso la stringa in tre sottostringhe x
, y
, z
, in modo tale che |x|>=1 && |xy|<=N
, si può ripetere y
tutte le volte che si desidera e la l'intera stringa apparterrà comunque a L
.
Una conseguenza del lemma di pompaggio è che non è possibile avere stringhe regolari nella forma a^Nb^Mc^N
, ovvero due sottostringhe aventi la stessa lunghezza separate da un'altra stringa. In qualsiasi modo si dividere tali stringhe in x
, y
e z
, non si può "pompa" y
, senza ottenere una stringa con un diverso numero di "a" e "c", lasciando così la lingua originale. È il caso, ad esempio, delle parentesi nelle espressioni regolari.
Sebbene sia perfettamente possibile usare una regex ricorsiva come ha pubblicato MizardX, per questo tipo di cose è molto più utile un parser. Originariamente i regex erano destinati all'uso con linguaggi regolari, essere ricorsivi o avere gruppi di bilanciamento è solo una patch.
Il linguaggio che definisce le regex valide è in realtà una grammatica libera dal contesto e dovresti usare un parser appropriato per gestirlo. Ecco un esempio di un progetto universitario per l'analisi di regex semplici (senza la maggior parte dei costrutti). Utilizza JavaCC. E sì, i commenti sono in spagnolo, anche se i nomi dei metodi sono piuttosto autoesplicativi.
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; }
}
È possibile inviare la regex a preg_match
cui restituirà false se la regex non è valida. Non dimenticare di usare il @
per sopprimere i messaggi di errore:
@preg_match($regexToTest, '');
//
.Il seguente esempio di Paul McGuire, originariamente dal wiki pyparsing, ma ora disponibile solo tramite la Wayback Machine , fornisce una grammatica per analizzare alcune regex, allo scopo di restituire il set di stringhe corrispondenti. Come tale, rifiuta quelle ri che includono termini di ripetizione illimitati, come '+' e '*'. Ma dovrebbe darti un'idea su come strutturare un parser che elabori 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()