Regexes wartbar machen
Ein wichtiger Fortschritt bei der Entmystifizierung der Muster, die zuvor als „reguläre Ausdrücke“ bezeichnet wurden, ist Perls /x
Regex-Flag, das manchmal (?x)
beim Einbetten geschrieben wird und Leerzeichen (Zeilenumbruch, Einrücken) und Kommentare zulässt. Dies verbessert die Lesbarkeit und damit die Wartbarkeit erheblich. Der Leerraum ermöglicht kognitives Chunking, sodass Sie sehen können, welche Gruppen mit welchen Gruppen verbunden sind.
Moderne Muster unterstützen jetzt auch relativ nummerierte und benannte Rückreferenzen. Das bedeutet, dass Sie keine Erfassungsgruppen mehr zählen müssen, um herauszufinden, ob Sie $4
oder benötigen \7
. Dies hilft beim Erstellen von Mustern, die in weitere Muster aufgenommen werden können.
Hier ist ein Beispiel für eine relativ nummerierte Erfassungsgruppe:
$ dupword = qr {\ b (?: (\ w +) (?: \ s + \ g {-1}) +) \ b} xi;
$ quote = qr {(["']) $ dupword \ 1} x;
Und hier ist ein Beispiel für den überlegenen Ansatz benannter Captures:
$dupword = qr{ \b (?: (?<word> \w+ ) (?: \s+ \k<word> )+ ) \b }xi;
$quoted = qr{ (?<quote> ["'] ) $dupword \g{quote} }x;
Grammatische Regexe
Das Beste ist , dass diese benannten Captures in einem (?(DEFINE)...)
Block platziert werden können, sodass Sie die Deklaration von der Ausführung einzelner benannter Elemente Ihrer Muster trennen können. Dadurch verhalten sie sich eher wie Unterprogramme innerhalb des Musters.
Ein gutes Beispiel für diese Art von „grammatikalischem Regex“ finden Sie in dieser und dieser Antwort . Diese sehen viel eher wie eine grammatikalische Erklärung aus.
Wie letzterer Sie erinnert:
… Stellen Sie sicher, dass Sie niemals Linienrauschmuster schreiben. Sie müssen nicht und Sie sollten nicht. Es kann keine Programmiersprache gewartet werden, die Leerzeichen, Kommentare, Unterprogramme oder alphanumerische Bezeichner verbietet. Verwenden Sie also all diese Dinge in Ihren Mustern.
Dies kann nicht überbetont werden. Wenn Sie diese Dinge nicht in Ihren Mustern verwenden, werden Sie natürlich oft einen Albtraum schaffen. Aber wenn Sie sie verwenden, brauchen Sie nicht.
Hier ist ein weiteres Beispiel für ein modernes grammatikalisches Muster, das zum Parsen von RFC 5322 verwendet wird: use 5.10.0;
$rfc5322 = qr{
(?(DEFINE)
(?<address> (?&mailbox) | (?&group))
(?<mailbox> (?&name_addr) | (?&addr_spec))
(?<name_addr> (?&display_name)? (?&angle_addr))
(?<angle_addr> (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
(?<group> (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
(?<display_name> (?&phrase))
(?<mailbox_list> (?&mailbox) (?: , (?&mailbox))*)
(?<addr_spec> (?&local_part) \@ (?&domain))
(?<local_part> (?&dot_atom) | (?"ed_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
\] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?"ed_pair))
(?<dtext> (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])
(?<atext> (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: \. (?&atext)+)*)
(?<text> [\x01-\x09\x0b\x0c\x0e-\x7f])
(?<quoted_pair> \\ (?&text))
(?<qtext> (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
(?<qcontent> (?&qtext) | (?"ed_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)
(?<word> (?&atom) | (?"ed_string))
(?<phrase> (?&word)+)
# Folding white space
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
# No whitespace control
(?<NO_WS_CTL> [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])
(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> \x0d \x0a)
(?<DQUOTE> ")
(?<WSP> [\x20\x09])
)
(?&address)
}x;
Ist das nicht bemerkenswert - und großartig? Sie können eine Grammatik im BNF-Stil direkt in Code übersetzen, ohne ihre grundlegende Struktur zu verlieren!
Wenn Ihnen moderne grammatikalische Muster immer noch nicht ausreichen, bietet das brillante Regexp::Grammars
Modul von Damian Conway eine noch sauberere Syntax mit überlegenem Debugging. Hier ist derselbe Code zum Parsen der Neufassung von RFC 5322 in ein Muster aus diesem Modul:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";
my $rfc5322 = do {
use Regexp::Grammars; # ...the magic is lexically scoped
qr{
# Keep the big stick handy, just in case...
# <debug:on>
# Match this...
<address>
# As defined by these...
<token: address> <mailbox> | <group>
<token: mailbox> <name_addr> | <addr_spec>
<token: name_addr> <display_name>? <angle_addr>
<token: angle_addr> <CFWS>? \< <addr_spec> \> <CFWS>?
<token: group> <display_name> : (?:<mailbox_list> | <CFWS>)? ; <CFWS>?
<token: display_name> <phrase>
<token: mailbox_list> <[mailbox]> ** (,)
<token: addr_spec> <local_part> \@ <domain>
<token: local_part> <dot_atom> | <quoted_string>
<token: domain> <dot_atom> | <domain_literal>
<token: domain_literal> <CFWS>? \[ (?: <FWS>? <[dcontent]>)* <FWS>?
<token: dcontent> <dtext> | <quoted_pair>
<token: dtext> <.NO_WS_CTL> | [\x21-\x5a\x5e-\x7e]
<token: atext> <.ALPHA> | <.DIGIT> | [!#\$%&'*+-/=?^_`{|}~]
<token: atom> <.CFWS>? <.atext>+ <.CFWS>?
<token: dot_atom> <.CFWS>? <.dot_atom_text> <.CFWS>?
<token: dot_atom> <.CFWS>? <.dot_atom_text> <.CFWS>?
<token: dot_atom_text> <.atext>+ (?: \. <.atext>+)*
<token: text> [\x01-\x09\x0b\x0c\x0e-\x7f]
<token: quoted_pair> \\ <.text>
<token: qtext> <.NO_WS_CTL> | [\x21\x23-\x5b\x5d-\x7e]
<token: qcontent> <.qtext> | <.quoted_pair>
<token: quoted_string> <.CFWS>? <.DQUOTE> (?:<.FWS>? <.qcontent>)*
<.FWS>? <.DQUOTE> <.CFWS>?
<token: word> <.atom> | <.quoted_string>
<token: phrase> <.word>+
# Folding white space
<token: FWS> (?: <.WSP>* <.CRLF>)? <.WSP>+
<token: ctext> <.NO_WS_CTL> | [\x21-\x27\x2a-\x5b\x5d-\x7e]
<token: ccontent> <.ctext> | <.quoted_pair> | <.comment>
<token: comment> \( (?: <.FWS>? <.ccontent>)* <.FWS>? \)
<token: CFWS> (?: <.FWS>? <.comment>)*
(?: (?:<.FWS>? <.comment>) | <.FWS>)
# No whitespace control
<token: NO_WS_CTL> [\x01-\x08\x0b\x0c\x0e-\x1f\x7f]
<token: ALPHA> [A-Za-z]
<token: DIGIT> [0-9]
<token: CRLF> \x0d \x0a
<token: DQUOTE> "
<token: WSP> [\x20\x09]
}x;
};
while (my $input = <>) {
if ($input =~ $rfc5322) {
say Dumper \%/; # ...the parse tree of any successful match
# appears in this punctuation variable
}
}
Es gibt viele gute Dinge auf der Perlre- Manpage , aber diese dramatischen Verbesserungen der grundlegenden Regex- Designfunktionen sind keineswegs nur auf Perl beschränkt. In der Tat ist die Manpage zu pcrepattern möglicherweise leichter zu lesen und deckt dasselbe Gebiet ab.
Moderne Muster haben fast nichts mit den primitiven Dingen zu tun, die Ihnen in Ihrer Klasse für endliche Automaten beigebracht wurden.