Es gibt ein populäres Zitat von Jamie Zawinski :
Einige Leute denken, wenn sie mit einem Problem konfrontiert werden: "Ich weiß, ich werde reguläre Ausdrücke verwenden." Jetzt haben sie zwei Probleme.
Wie ist dieses Zitat zu verstehen?
Es gibt ein populäres Zitat von Jamie Zawinski :
Einige Leute denken, wenn sie mit einem Problem konfrontiert werden: "Ich weiß, ich werde reguläre Ausdrücke verwenden." Jetzt haben sie zwei Probleme.
Wie ist dieses Zitat zu verstehen?
Antworten:
Einige Programmiertechnologien werden von Programmierern im Allgemeinen nicht gut verstanden ( reguläre Ausdrücke , Gleitkomma , Perl , AWK , IoC ... und andere ).
Dies können erstaunlich leistungsstarke Tools sein, um die richtigen Probleme zu lösen. Insbesondere reguläre Ausdrücke sind sehr nützlich, um reguläre Sprachen abzugleichen. Und da ist der Kern des Problems: Nur wenige Menschen können eine reguläre Sprache beschreiben (es ist Teil der Informatiktheorie / Sprachwissenschaft, die lustige Symbole verwendet - das können Sie in der Chomsky-Hierarchie nachlesen ).
Wenn Sie diese Dinge falsch anwenden, ist es unwahrscheinlich, dass Sie Ihr ursprüngliches Problem tatsächlich gelöst haben. Die Verwendung eines regulären Ausdrucks zur Übereinstimmung mit HTML (ein viel zu häufiges Vorkommen) führt dazu, dass Sie Randfälle verpassen. Und jetzt haben Sie immer noch das ursprüngliche Problem, das Sie nicht gelöst haben, und einen weiteren subtilen Fehler, der durch die Verwendung der falschen Lösung behoben wurde.
Das soll nicht heißen, dass reguläre Ausdrücke nicht verwendet werden sollten, sondern dass man versuchen sollte zu verstehen, welche Probleme sie lösen können und welche nicht und mit Bedacht.
Der Schlüssel zur Wartung der Software ist das Schreiben von wartbarem Code. Die Verwendung regulärer Ausdrücke kann diesem Ziel entgegenwirken. Wenn Sie mit regulären Ausdrücken arbeiten, haben Sie einen Mini-Computer (insbesondere einen nicht deterministischen Automaten mit endlichen Zuständen ) in einer speziellen domänenspezifischen Sprache geschrieben. Es ist einfach, das 'Hallo Welt'-Äquivalent in dieser Sprache zu schreiben und ein rudimentäres Vertrauen in diese Sprache zu gewinnen. Um jedoch zu vermeiden, dass zusätzliche Fehler geschrieben werden, die sehr schwer zu identifizieren und zu beheben sind (weil Sie sind nicht Teil des Programms, in dem sich der reguläre Ausdruck befindet.
Jetzt haben Sie ein neues Problem. Sie haben das Tool des regulären Ausdrucks ausgewählt, um es zu lösen (wenn es unangemessen ist), und Sie haben jetzt zwei Bugs, die beide schwerer zu finden sind, weil sie in einer anderen Abstraktionsebene versteckt sind.
Reguläre Ausdrücke - insbesondere nicht triviale - sind möglicherweise schwer zu codieren, zu verstehen und zu pflegen. Sie müssen sich nur die Anzahl der Fragen ansehen, die auf dem Stack Overflow-Tag angegeben sind und bei [regex]
denen der Fragesteller angenommen hat, dass die Antwort auf sein Problem ein regulärer Ausdruck ist, und die anschließend hängengeblieben sind. In vielen Fällen kann (und sollte) das Problem auf andere Weise gelöst werden.
Wenn Sie sich also für die Verwendung eines regulären Ausdrucks entscheiden, treten zwei Probleme auf:
Grundsätzlich denke ich, er meint, Sie sollten nur einen regulären Ausdruck verwenden, wenn es keine andere Möglichkeit gibt, Ihr Problem zu lösen. Eine andere Lösung wird wahrscheinlich einfacher zu programmieren, zu warten und zu unterstützen sein. Es kann langsamer oder weniger effizient sein, aber wenn dies nicht kritisch ist, sollte die einfache Wartung und der Support das übergeordnete Anliegen sein.
Es ist meist ein Scherz, wenn auch mit einem Körnchen Wahrheit.
Es gibt einige Aufgaben, für die reguläre Ausdrücke hervorragend geeignet sind. Ich habe einmal 500 manuell geschriebene Zeilen rekursiven Parser-Codes durch einen regulären Ausdruck ersetzt, dessen vollständiges Debugging etwa 10 Minuten in Anspruch nahm. Man sagt, reguläre Ausdrücke seien schwer zu verstehen und zu debuggen, aber angemessen angewandte sind bei weitem nicht so schwer zu debuggen wie ein riesiger handgefertigter Parser. In meinem Beispiel dauerte es zwei Wochen, um alle Edge-Fälle der Nicht-Regex-Lösung zu debuggen.
Um jedoch Onkel Ben zu paraphrasieren:
Mit großer Ausdruckskraft geht eine große Verantwortung einher.
Mit anderen Worten, reguläre Ausdrücke verleihen Ihrer Sprache mehr Ausdruckskraft, aber das überträgt dem Programmierer mehr Verantwortung, den am besten lesbaren Ausdrucksmodus für eine bestimmte Aufgabe zu wählen.
Einige Dinge sehen auf den ersten Blick wie eine gute Aufgabe für reguläre Ausdrücke aus, sind es aber nicht. Zum Beispiel alles mit verschachtelten Token wie HTML. Manchmal wird ein regulärer Ausdruck verwendet, wenn eine einfachere Methode klarer ist. Zum Beispiel string.endsWith("ing")
ist leichter zu verstehen als der entsprechende reguläre Ausdruck. Manchmal versuchen die Leute, ein großes Problem in ein einzelnes Regex zu packen, wo es angemessener ist, es in Stücke zu zerbrechen. Manchmal schaffen es die Benutzer nicht, geeignete Abstraktionen zu erstellen, und wiederholen einen regulären Ausdruck immer wieder, anstatt eine gut benannte Funktion zu erstellen, die denselben Job ausführt (möglicherweise intern mit einem regulären Ausdruck implementiert).
Aus irgendeinem Grund neigen reguläre Ausdrücke dazu, einen toten Winkel zu normalen Softwareentwicklungsprinzipien wie Single Responsibility und DRY zu bilden. Das ist der Grund, warum selbst Menschen, die sie lieben, sie manchmal als problematisch empfinden.
Jeff Atwood legt eine andere Interpretation in einem Blog-Beitrag vor, der genau dieses Zitat behandelt: Reguläre Ausdrücke: Jetzt haben Sie zwei Probleme (danke an Euphoric für den Link)
Wenn wir den vollständigen Text von Jamies Beiträgen im ursprünglichen Thread von 1997 analysieren, finden wir Folgendes:
Perls Natur ermutigt die Verwendung regulärer Ausdrücke fast unter Ausschluss aller anderen Techniken; Sie sind bei weitem der "offensichtlichste" Weg (zumindest für Menschen, die es nicht besser wissen), um von Punkt A nach Punkt B zu gelangen.
Das erste Zitat ist zu glitschig, um ernst genommen zu werden. Aber dem stimme ich voll und ganz zu. Hier ist der Punkt, den Jamie anstrebte: Nicht dass reguläre Ausdrücke per se böse sind, sondern dass übermäßiger Gebrauch von regulären Ausdrücken böse ist.
Auch wenn Sie es vollständig reguläre Ausdrücke verstehen, laufen Sie in The Golden Hammer Problem, ein Problem mit regulären Ausdrücken zu lösen versuchen, wenn es wäre einfacher und klarer gewesen sein , die gleiche Sache mit regelmäßigem Code zu tun (siehe auch Coding: Regex Verwendung Regex-Missbrauch ).
Es gibt einen weiteren Blog-Beitrag, der sich mit dem Kontext des Zitats befasst und ausführlicher ist als Atwood: Jeffrey Friedls Blog: Quelle des berühmten Zitats „Jetzt hast du zwei Probleme“
Es gibt ein paar Dinge, die mit diesem Zitat geschehen.
Das Zitat ist eine Wiederholung eines früheren Witzes:
Immer wenn jemand mit einem Problem konfrontiert wird, sagen manche Leute "Lasst uns AWK benutzen." Jetzt haben sie zwei Probleme. - D. Tilbrook
Es ist ein Witz und eine echte Ausgrabung, aber es ist auch eine Möglichkeit, Regex als schlechte Lösung hervorzuheben, indem es mit anderen schlechten Lösungen verknüpft wird. Es ist ein großartiger, nur ernster Moment.
Für mich - wohlgemerkt, dieses Zitat ist absichtlich offen für Interpretationen - ist die Bedeutung eindeutig. Das Problem ist noch nicht gelöst, wenn Sie lediglich die Idee der Verwendung eines regulären Ausdrucks ankündigen. Darüber hinaus haben Sie die kognitive Komplexität des Codes erhöht, indem Sie eine zusätzliche Sprache mit Regeln hinzugefügt haben, die sich von der verwendeten Sprache unterscheiden.
Obwohl es witzig ist, müssen Sie die Komplexität einer Nicht-Regex-Lösung mit der Komplexität der Regex-Lösung und der zusätzlichen Komplexität des Einbindens von Regexen vergleichen. Trotz der zusätzlichen Kosten für das Hinzufügen von Regexen kann es sich lohnen, ein Problem mit einem Regex zu lösen.
Regelmäßige Ausdrücke lassen keinen anderen unformatierten Inhalt erkennen, in der Tat ist es unwahrscheinlich, dass dieses Textstück gelesen wird, da einige Implementierungen keine Formatierung zulassen und die Menschen überhaupt nicht wissen, was sie tun.
(Reguläre Ausdrücke sind nicht schlechter zu lesen oder zu pflegen als jeder andere unformatierte Inhalt. In der Tat ist ein regulärer Ausdruck wahrscheinlich einfacher zu lesen als dieser Text hier. Leider haben sie einen schlechten Ruf, da einige Implementierungen Formatierungen und Personen im Allgemeinen nicht zulassen Ich weiß nicht, dass du es schaffst.)
Hier ist ein triviales Beispiel:
^(?:[^,]*+,){21}[^,]*+$
Was sowieso nicht so schwer zu lesen oder zu warten ist, aber noch einfacher, wenn es so aussieht:
(?x) # enables comments, so this whole block can be used in a regex.
^ # start of string
(?: # start non-capturing group
[^,]*+ # as many non-commas as possible, but none required
, # a comma
) # end non-capturing group
{21} # 21 of previous entity (i.e. the group)
[^,]*+ # as many non-commas as possible, but none required
$ # end of string
Das ist ein übertriebenes Beispiel (Kommentieren $
ist vergleichbar mit Kommentieren i++
), aber es sollte eindeutig kein Problem geben, das zu lesen, zu verstehen und aufrechtzuerhalten.
Solange klar ist, wann reguläre Ausdrücke geeignet sind und wann sie eine schlechte Idee sind, ist nichts daran auszusetzen, und meistens gilt das Zitat von JWZ nicht wirklich.
*+
? Wie ist das anders (funktional) als nur *
?
*+
In diesem Fall hat das buchstäblich keinen Sinn . Alles ist verankert und kann in einem Durchgang von einem Automaten abgeglichen werden, der bis zu 22 zählen kann. Der richtige Modifikator für diese Nicht-Kommasätze ist einfach alt *
. (Außerdem sollte es hier keine Unterschiede zwischen gierigen und nicht-gierigen Matching-Algorithmen geben. Dies ist ein äußerst einfacher Fall.)
Neben der Antwort von ChrisF, dass reguläre Ausdrücke "schwer zu codieren, zu verstehen und zu pflegen sind", ist noch schlimmer: Sie sind nur so mächtig, dass die Leute versuchen, sie zum Parsen von Dingen zu verwenden, die sie nicht können, wie beispielsweise HTML. Siehe die zahlreichen Fragen zu SO zu "Wie analysiere ich HTML?" Zum Beispiel die epischste Antwort in SO!
Reguläre Ausdrücke sind sehr mächtig, haben aber ein kleines und ein großes Problem. Sie sind schwer zu schreiben und fast unmöglich zu lesen.
Im besten Fall löst die Verwendung des regulären Ausdrucks das Problem, sodass Sie nur das Wartungsproblem des komplizierten Codes haben. Wenn Sie den regulären Ausdruck nicht richtig verstehen, haben Sie sowohl das ursprüngliche Problem als auch das Problem mit unlesbarem Code, der nicht funktioniert.
Manchmal werden reguläre Ausdrücke als Nur-Schreib-Code bezeichnet. Angesichts eines regulären Ausdrucks, der korrigiert werden muss, ist es oft schneller, von vorne zu beginnen, als zu versuchen, den Ausdruck zu verstehen.
Das Problem ist, dass Regex ein kompliziertes Biest ist und Sie Ihr Problem nur lösen, wenn Sie Regex perfekt einsetzen. Andernfalls treten zwei Probleme auf: das ursprüngliche Problem und der reguläre Ausdruck.
Sie behaupten, dass es die Arbeit von hundert Codezeilen erledigen kann, aber Sie könnten auch argumentieren, dass 100 Zeilen klarer, präziser Code besser sind als eine Zeile regulärer Ausdrücke.
Wenn Sie einen Beweis dafür benötigen: Sie können diesen SO Classic ausprobieren oder einfach den SO Regex-Tag durchkämmen
Die Bedeutung besteht aus zwei Teilen:
Wenn Sie 2014 danach fragen, wäre es interessant, sich auf die Programmiersprachenideologien des Kontextes von 1997 im Vergleich zum heutigen Kontext zu konzentrieren. Ich werde hier nicht auf diese Debatte eingehen, aber die Meinungen über Perl und Perl selbst haben sich stark geändert.
Um jedoch in einem Kontext von 2013 zu bleiben ( de l'eau a coulé sous les ponts depuis), würde ich vorschlagen, sich auf die Nachstellung von Zitaten mit einem berühmten XKCD-Comic zu konzentrieren, der ein direktes Zitat von Jamie Zawinski ist :
Zuerst hatte ich Probleme, diesen Comic zu verstehen, weil er sich auf das Zawinski-Zitat und ein Zitat eines Jay-z-Liedtextes und eine Referenz von GNU- program --help -z
Flag 2 bezog . Es war also zu viel Kultur, als dass ich ihn verstehen könnte.
Ich wusste, dass es Spaß machte, ich fühlte es, aber ich wusste nicht wirklich warum. Die Leute machen oft Witze über Perl und Regexes, vor allem, weil es nicht die angesagteste Programmiersprache ist und sie nicht wirklich wissen, warum es Spaß machen soll ... Vielleicht, weil Perl-Händler alberne Dinge tun .
Das anfängliche Zitat scheint also ein sarkastischer Witz zu sein, der auf realen Problemen (Schmerzen?) Beruht, die durch das Programmieren mit Werkzeugen verursacht werden, die weh tun. So wie ein Hammer einen Maurer verletzen kann, programmiert er mit Werkzeugen, die ein Entwickler nicht wählen würde, wenn er verletzen könnte (das Gehirn, die Gefühle). Manchmal große Debatten über das Tool ist die beste auftritt, aber es ist fast wertlos weil es ein Problem ist Ihren Geschmack oder Ihr Programmierteam Geschmack , kultureller oder wirtschaftlicher Gründe. Ein weiterer hervorragender XKCD-Comic dazu:
Ich kann verstehen, dass Menschen Schmerzen mit Regexen haben, und sie glauben, dass ein anderes Tool besser für das geeignet ist, wofür Regexen entwickelt wurden. Da @ karl-bielefeldt Ihre Frage mit großer Expressivität beantwortet, kommt eine große Verantwortung auf , und Regexes sind davon besonders betroffen. Wenn ein Entwickler sich nicht darum kümmert, wie er mit regulären Ausdrücken umgeht, wird es für Leute, die den Code später pflegen, schlimm sein.
Ich werde mit dieser Antwort über die Nachstellung von Zitaten durch ein Zitat abschließen, das ein typisches Beispiel aus Perl Best Practices von Damian Conw ay zeigt (ein Buch von 2005).
Er erklärt das Schreiben eines Musters wie folgt:
m{'[^\\']*(?:\\.[^\\']*)*'}
... ist nicht akzeptabler als ein Programm wie dieses zu schreiben :
sub'x{local$_=pop;sub'_{$_>=$_[0
]?$_[1]:$"}_(1,'*')._(5,'-')._(4
,'*').$/._(6,'|').($_>9?'X':$_>8
?'/':$")._(8,'|').$/._(2,'*')._(
7,'-')._(3,'*').$/}print$/x($=).
x(10)x(++$x/10).x($x%10)while<>;
Aber es kann umgeschrieben werden , es ist immer noch nicht schön, aber zumindest ist es jetzt überlebensfähig.
# Match a single-quoted string efficiently...
m{ ' # an opening single quote
[^\\']* # any non-special chars (i.e., not backslash or single quote)
(?: # then all of...`
\\ . # any explicitly backslashed char
[^\\']* # followed by any non-special chars
)* # ...repeated zero or more times
' # a closing single quote
}x
Diese Art von rechteckigem Code ist das zweite Problem, bei dem es sich nicht um reguläre Ausdrücke handelt, die klar, wartbar und lesbar formatiert werden können.
/* Multiply the first 10 values in an array by 2. */ for (int i = 0 /* the loop counter */; i < 10 /* continue while it is less than 10 */; ++i /* and increment it by 1 in each iteration */) { array[i] *= 2; /* double the i-th element in the array */ }
Wenn es eine Sache gibt, die Sie aus der Informatik lernen sollten, dann ist das die Chomsky-Hierarchie . Ich würde sagen, dass alle Probleme mit regulären Ausdrücken von dem Versuch herrühren, damit eine kontextfreie Grammatik zu analysieren. Wenn Sie den Verschachtelungsebenen in CFG ein Limit auferlegen können (oder glauben, Sie können ein Limit auferlegen), erhalten Sie diese langen und komplexen regulären Ausdrücke.
Reguläre Ausdrücke eignen sich eher für die Tokenisierung als für das vollständige Parsen.
Aber eine überraschend große Menge von Dingen, die Programmierer analysieren müssen, können von einer regulären Sprache analysiert werden (oder, schlimmer noch, fast von einer regulären Sprache analysiert werden und wenn Sie nur ein bisschen mehr Code schreiben ...).
Wenn man sich also an "aha, ich muss Text auseinander nehmen, ich verwende einen regulären Ausdruck" gewöhnt hat, ist es einfach, diesen Weg zu gehen, wenn man etwas benötigt, das näher an einem Push-Down-Automaten, einem CFG-Parser oder einem anderen liegt noch mächtigere Grammatiken. Das endet normalerweise in Tränen.
Ich denke also, dass das Zitat nicht so sehr reguläre Ausdrücke zuschlägt, sie haben ihren Nutzen (und sind gut genutzt, sie sind in der Tat sehr nützlich), aber das übermäßige Vertrauen in reguläre Ausdrücke (oder insbesondere die unkritische Wahl von ihnen). .
jwz ist mit diesem Zitat einfach von seinem Rocker. reguläre Ausdrücke unterscheiden sich nicht von anderen Sprachfeatures - einfach zu verwechseln, schwierig zu verwenden, manchmal mächtig, manchmal unangemessen, oft gut dokumentiert, oft nützlich.
Das Gleiche gilt für Gleitkomma-Arithmetik, Closures, Objektorientierung, asynchrone E / A oder alles andere, was Sie benennen können. Wenn Sie nicht wissen, was Sie tun, können Programmiersprachen Sie traurig machen.
Wenn Sie der Meinung sind, dass reguläre Ausdrücke schwer zu lesen sind, lesen Sie die entsprechende Parser-Implementierung, um das betreffende Muster zu verarbeiten. Regexes gewinnen oft, weil sie kompakter sind als vollständige Parser ... und in den meisten Sprachen sind sie auch schneller.
Lassen Sie sich nicht davon abhalten, reguläre Ausdrücke (oder andere Sprachfunktionen) zu verwenden, da ein sich selbst fördernder Blogger uneingeschränkte Aussagen macht. Probieren Sie es aus und sehen Sie, was für Sie funktioniert.
Meine liebste und ausführlichste Antwort darauf gibt der berühmte Rob Pike in einem Blog-Beitrag, der aus einem internen Google-Codekommentar stammt: http://commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- and.html
Die Zusammenfassung ist, dass es nicht so ist, dass sie schlecht sind , aber sie werden häufig für Aufgaben verwendet, für die sie nicht unbedingt geeignet sind, insbesondere wenn es darum geht, Eingaben zu lexen und zu analysieren.
Reguläre Ausdrücke sind schwer zu schreiben, schwer gut zu schreiben und können im Vergleich zu anderen Technologien teuer sein. Lexer hingegen sind recht einfach richtig zu schreiben (wenn auch nicht so kompakt) und sehr einfach zu testen. Erwägen Sie die Suche nach alphanumerischen Bezeichnern. Es ist nicht allzu schwer, den regulären Ausdruck (so etwas wie "[a-ZA-Z _] [a-ZA-Z_0-9] *") zu schreiben, aber es ist wirklich nicht viel schwieriger, ihn als einfache Schleife zu schreiben. Die Leistung der Schleife wird jedoch viel höher sein und viel weniger Code unter der Decke beinhalten. Eine Bibliothek mit regulären Ausdrücken ist eine große Sache. Die Verwendung eines Identifikators zum Parsen entspricht der Verwendung eines Ferrari, um Milch in den Laden zu holen.
Er sagt viel mehr als das und argumentiert, dass reguläre Ausdrücke nützlich sind, z. B. um Muster in Texteditoren auf einmal abzugleichen, aber selten in kompiliertem Code verwendet werden sollten, und so weiter. Es ist eine Lektüre wert.
Dies hängt mit Alan Perlis 'Epigramm Nr. 34 zusammen:
Die Zeichenfolge ist eine strenge Datenstruktur, und überall, wo sie übergeben wird, kommt es zu doppelten Prozessen. Es ist ein perfektes Fahrzeug zum Verstecken von Informationen.
Wenn Sie also die Zeichenfolge als Datenstruktur auswählen (und natürlich Regex-basierten Code als Algorithmus, um sie zu manipulieren), haben Sie ein Problem, auch wenn es funktioniert: Schlechtes Design um eine unangemessene Darstellung von Daten, die schwer zu bearbeiten sind verlängern und ineffizient.
Oft funktioniert es jedoch nicht: Das ursprüngliche Problem ist nicht gelöst, und in diesem Fall treten zwei Probleme auf.
Regexe werden häufig zum schnellen und unsauberen Parsen von Text verwendet. Sie eignen sich hervorragend zum Ausdrücken von Mustern, die etwas komplexer sind als nur eine einfache Zeichenfolgenübereinstimmung.
Mit zunehmender Komplexität von Regexen erheben sich jedoch mehrere Probleme.
Daher ist es allzu einfach, mit einem Textverarbeitungsproblem zu beginnen, reguläre Ausdrücke darauf anzuwenden und am Ende zwei Probleme zu lösen, das ursprüngliche Problem, das Sie zu lösen versuchten, und die regulären Ausdrücke zu behandeln, die zu lösen versuchen (aber nicht richtig lösen). das ursprüngliche Problem.