Delimiter/Pattern Aufbauerklärung gesucht
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
anfang
array
ausdruck
beispiel
buchstabe
code
dank
expression
gruppe
http
klammern
nutzen
punkt
sagen
stehen
string
url
vorkommen
zeichen
zeile
-
Hallo,
da ich schon seit längerem mit php arbeite, stolpere ich immer häufiger über Delimiter/Pattern-Strings in Befehlen wie explode, split, o.ä.
Nun möchte ich gerne wissen, wie diese Strings (auf php.net wird es bei split "pattern" genannt und bei explode "delimiter") aufgebaut sind.
Als Beispiele zur Verdeutlichung wären vielleicht Scripts zu folgenden Problemen hilfreich:
-> Überprüfung, ob ein String dem Muster einer E-Mail-Adresse entspricht
-> Überprüfung, ob String dem Muster einer Rechenaufgabe entspricht
-> Datetime-Format mithilfe von list() in einer Befehlszeile in alle Bestandteile zerlegen (stunde, tag, etc.)
Für meine bisherigen Probleme musste ich mir immer bei google zu diesem Fall einen 'pattern', bzw. 'delimiter' zurecht-suchen, aber ich möchte in Zukunft diese selber erstellen.
Und was genau ist der Unterschied zwischen einem Pattern und einem Delimiter?
lg Ole -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Ich weiß nicht genau was du hier mit Delimiter und Pattern meinst oder eher verstehe ich nicht was letzteres mit explode und split zu tun hat.
Aber allgemein: Ein Delimiter ist ein Trennzeichen. Ein explode('|', $string) trennt $string also an jedem |. Mit Pattern meinst du wahrscheinlich ein PCRE (Perl Compatible Regular Expression), im Volksmund auch gerne regulärer Ausdruck oder RegEx(p) genannt. In dem Kontext von PCRE hat Delimiter dann noch eine andere Bedeutung (es ist das Ding, das den regulären Ausdruck an sich von den Modifiers trennt...)
Als Beispiele hast du aber Sachen gewählt, die nicht gerade einfach sind... eine Email-Adresse ist hochkomplex. Ein RFC-konformer PCRE ist etwa 200 Zeichen lang und für den Normalbenutzer völlig unverständlich und langsam. Zudem sind nicht alle gebräuchlichen Emails RFC-komform, insbesondere im Bezug auf Sonderzeichen ;)
Für Emails benutze ich persönlich
/[^@]+@(?:[^.]+\.)+[A-Za-z]{2,6}/
Dieser reguläre Ausdruck ist schon sehr frei und würde viele zulassen, die nicht gültig sind. Dennoch verbietet dieser freie reguläre Ausdruck bereits einige Emails (zum Beispiel "nikic\@home"@example.org oder nikic@[irgendwas]). Daher würde ich dir gerade bei Emails eher raten filter_var zur Validierung zu nutzen. (Am besten in Verbindung mit checkdnsrr, um zu prüfen, ob die Domain existiert.)
Bei der Rechenaufgabe wird es noch viel komplizierter. Auch wenn du nur die grundlegenden Operationen wie + - / * zulässt, dann hast du immer noch das Problem der Klammern:
3 + 7 * (2 + 5 * (3 + 2))
Du müsstest die Klammern folglich rekursiv auflösen. PCRE lässt dies zu, es ist aber auf jeden Fall nicht einfach.
/(?:[0-9]+|\((?R)\))(?:\s*[-+\/*]\s*(?:[0-9]+|\((?R)\)))*/
Vereinfacht (dafür mit einer Capturing Group:
/([0-9]+|\((?R)\))(?:\s*[-+\/*]\s*(?1))*/
Du siehst auf jeden Fall, dass das nicht unbedingt der einfachste reguläre Ausdruck ist ;)
Beitrag zuletzt geändert: 17.10.2010 13:38:24 von nikic -
Danke schonmal für deine Mühen, du hast schon recht gut verstanden, was ich möchte, aber ich möchte den Grundaufbau eines Strings, wie diesen:
verstehen./(?:[0-9]+|\((?R)\))(?:\s*[-+\/*]\s*(?:[0-9]+|\((?R)\)))*/
was ist mit
gemeint, noch genauer: was ist mit ?:[...] gemeint, was gibt es außerdem noch.../(?:[0-9]+|\((?R)\))
kurz: wie sind solche strings aufgebaut, woraus setzen sie sich zusammen und was bewirken die einzelnen Teile?
Kleinere Nebenfragen:
-> 0-9; a-z; A-Z was gibt es sonst noch in dieser Richtung?
-> was bedeutet 'Capturing Group' in diesem Fall und was sind dessen Nachteile?
Kann mir vielleicht irgendjemand, der den Aufbau versteht sagen, wo er es gelernt/erfahren hat?
Von Delimiter und Pattern habe ich geredet, da dieser String zur Zerlegung auf php.net unter explode und split so genannt wird. -
Hier eine Erklärung:
http://de.wikipedia.org/wiki/Regul%C3%A4rer_Ausdruck#Regul.C3.A4re_Ausdr.C3.BCcke_in_der_Praxis
Hier noch weitere:
http://www.sql-und-xml.de/regex/escapezeichen-klassen-klammern.html
http://www.nettz.de/Service/regexp/
Einfach nach "Regular Expression" suchen.
Kann mir vielleicht irgendjemand, der den Aufbau versteht sagen, wo er es gelernt/erfahren hat?
Mit Regular Expression bin ich zum ersten Mal durch sed in Berührung gekommen.
http://de.wikipedia.org/wiki/Sed_%28Unix%29
Ciao,
Franco
Beitrag zuletzt geändert: 18.10.2010 6:23:29 von franco-bez -
@ole-reglitzki: Ich wollte damit nur sagen, dass ein RegEx für Mathematische Ausdrücke nicht gerade das richtige ist um anzufangen... erklären kann ich dir aber den für die Emails, da steckt nicht so viel hinter:
/ # Delimiter. Damit beginnt und endet der eigentliche reguläre Ausdruck. Man sollte möglichst einen wählen, der im regulären Ausdruck nicht vorkommt, weil man ihn sonst escapen muss mit \/ [^@]+ # [^@] bedeutet ein beliebiges Zeichen *außer* @. + bedeutet ein oder mehr Zeichen. D.h. das ganze ist "Ein oder mehr beliebige Zeichen außer @" @ # ein @ (?: # eine Non-Capturing-Group. Mit ( und ) erstellst du Gruppen. Normalerweise wird alles in solchen Gruppen in einem Ergebnisarray gespeichert. Da diese Information hier aber nicht brauchst, nutzen wir eine Gruppe, die die Information *nicht* speichert und somit schneller ist. Wenn dich das zu sehr verwirrt, benutze einfach (), es geht auch ;) [^.]+ # Ein oder mehr Zeichen außer einem Punkt \. # Ein Punkt. Beachte, dass man den Punkt mit \. escapen muss. Das liegt daran, dass der Punkt in RegEx eine besondere Bedeutung hat )+ # Die gesamte Gruppe, also Ein oder mehr Zeichen gefolgt von einem Punkt kann mehrfach (und mindestens einmal) vorkommen [A-Za-z]{2,6} # anschließend kommt die TLD, so wie .de oder .museum. Die haben immer zwei bis sechs Buchstaben. /x
Das ganz am Ende (das /x) ist einmal der schließende Delimiter (du erinnerst dich noch an das / am Anfang?) und ein Modifier: x. Hinter dem schließenden Delimiter können verschiedene Modifiers platziert werden. x steht hier für PCRE_EXTENDED und bedeutet, dass Whitespace im RegEx ignoriert wird und ich Kommentare mit # machen kann ;)
Also hier nochmal was die einzelnen Bestandteile des RegEx matchen:
/ [^@]+ nikic @ @ (?:[^.]+\.)+ mail.lima-city. [A-Za-z]{2,6} de /
Die eine Nebenfrage habe ich versucht oben zu beantworten, die andere hier:
Du kannst da eigentlich so ziemlich alles nehmen, aber nur A-Z 0-9 und a-z sind geläufig. Wenn es zum Beispiel nur eine Zah von 1 bis 9 sein darf schreibst du [1-9]. Wenn es ein String mit 1-9 und Großbuchstaben sein darf, dann benutzt du [1-9A-Z].
Möglich ist auch (aber wie gesagt unüblich) auch andere Zeichenspannen zu nutzen, zum Beispiel [A-z]. Das solltest du nicht machen, wenn du nicht genau weißt, was du tust. Zwischen A und z liegen nach ASCII nämlich nicht nur Buchstaben, sondern auch andere Zeichen ;) Ebenso kannst du [§-&] schreiben, das wird dann auch die jeweilige Zeichenspanne nach ASCII nutzen ;) Aber wie gesagt, ich würde lieber erstmal bei Buchstaben und Zahlen bleiben ;)
Beitrag zuletzt geändert: 18.10.2010 12:18:50 von nikic -
Danke für die Links!
besonders http://www.sql-und-xml.de/regex/escapezeichen-klassen-klammern.html hat mir viel geholfen!
Und vielen Dank für deine Mühen, nikic!
Dann möchte ich jetzt noch einmal ein paar Versuche starten (EDIT: ich ignoriere im code jetzt mal die richtige Verwendung von Kommentaren und whitespaces und habe auch zum Teil Zeilenangaben eingefügt):
/ [^@]+ Alle Zeichen, außer @ ([^@]) werden beliebig oft wiederholt, d. h. sie kommen 1-x mal vor (+) @ @ (?:[^.]+\.)+ [^.] bedeutet, dass 1-x (+) beliebige Zeichen, außer Zeilenumbrüchen vorkommen dürfen, aber am Ende muss ein Punkt stehen. Damit es bei dem Ersten Punkt nicht abbricht und auf die Endung geprüft wird, darf das ganze 1-x mal vorkommen. [A-Za-z]{2,6} Es müssen Anschließend noch 2-6 klein- oder groß-Buchstaben folgen (de, com, etc.) /
1 / 2 ( ENTWEDER 3 ?:[0-9]+ beliebig viele Ziffern (min. 1) (was bedeutet "?:"? Dafür habe ich keine verständliche Erklärung gefunden) 4 | ODER 5 \((?R)\) Was bedeutet ?R -> ich vermute mal: Rekursivausführung, d. h.: Wenn (...) vorkommt, soll er für das Innere der Klammern das ganze noch einmal ausführen 6 ) 7 8 ( 9 ?:\s* Es können whitespaces kommen (was bedeutet "?:"? Dafür habe ich keine verständliche Erklärung gefunden) 10 [-+\/*] Es kommt ein Rechenzeichen (wieso "\/"? nach www.sql-und-xml.de verliert / innerhalb [] seine Bedeutung, oder für welches Rechenzeichen steht '\'?) 11 \s* Es können whitespaces kommen 12 13 ( ENTWEDER 14 ?:[0-9]+ siehe oben 15 | ODER 16 \((?R)\) siehe oben 17 ) 18 19 )* Das ganze (die zweite große Klammer) kann beliebig oft (0-x) mal vorkommen 20 /
Meine Fragen stehen zum Teil im code, aber sonst noch:
müssten Zeile 3 und 14 durch
(?:[0-9]+([.,][0-9]+)?)
ersetzt werden, damit Kommastellen (mit PUNKT oder KOMMA) erlaubt sind?
Damit negative Zahlen am Anfang ohne Klammern stehen können bräuchte man dann in Zeile 3:
(?:[-+]?[0-9]+([.,][0-9]+)?)
Also der ganze Zusammenhängende String:
/((?:[-+]?[0-9]+([.,][0-9]+)?)|\((?R)\))(?:\s*[-+\/*]\s*(?:([0-9]+([.,][0-9]+)?)|\((?R)\)))*/
Allerdings bin ich mir gerade nicht sicher, ob Zeile 2-6 überhaupt gebraucht werden, wenn Zeile 9-11 hinter Zeile 17 kommen und mit einem '?' versehen werden, - um eine negative Anfangszahl (und auch multi- / dividieren mit negativen Zahlen, ohne Klammersetzung) zu akzeptieren, wird noch eine kleine Veränderung vorgenommen - also:
/(((?:[-+]?[0-9]+([.,][0-9]+)?)|\((?R)\))(?:\s*[-+\/*]\s*)?)*/
Auseinandergepflückt:
1 / 2 ( 3 ( 4 (?: 5 [-+]? 6 [0-9]+ 7 ( 8 [.,] 9 [0-9]+ 10 )? 11 ) 12 | 13 \( 14 (?R) 15 \) 16 ) 17 (?: 18 \s* 19 [-+\/*] 20 \s* 21 )? 22 )* 23 /
Danke für Korrekturen und Antworten ;)
lg Ole
Beitrag zuletzt geändert: 18.10.2010 18:23:32 von ole-reglitzki -
Okay. Grundsätzlich ist alles, was du da sagst richtig :) Ich will dann nurnoch die Fragen beantworten und ein paar geringfügige Korrekturen anbringen ;)
[^.] bedeutet, dass 1-x (+) beliebige Zeichen, außer Zeilenumbrüchen vorkommen dürfen
Das wird wohl eher ein Tippfehler sein, du meintest sicher "außer Punkte" ;)
beliebig viele Ziffern (min. 1) (was bedeutet "?:"? Dafür habe ich keine verständliche Erklärung gefunden)
Ich hab versucht das in meinem vorherigen Post zu erklären, scheinbar nicht sonderlich gut :D Also, nochmal: Das ?: gehört zu der Klammer, nicht zu dem Darauffolgendem. Zerlegen würde ich es also eher:
(?: [0-9]+ | \((?R)\) )
Also. Erstmal musst du wissen, dass man in RegEx Ausdrücke gruppieren kann, mit Hilfe von Klammern. Das ist recht logisch ^^ Wie in der Mathematik halt. Die einfachste Form der Gruppierung ist (Ausdruck). Hierbei wird der Ausdruck gematcht und das Ergebnis in ein Ergebnisarray gepackt. Also:
preg_match('/([^@]+)@((?:[^.]+\.)+[A-Za-z]{2,6})/', 'nikic@example.org', $matches)
Beachte, wie ich da zwei weitere Gruppen eingefügt habe zum ursprünglichen RegEx. Wenn ich jetzt ein var_dump($matches) mache, dann bekomme ich irgendwas wie:
Array( 'nikic@example.org', // das erste Element ist immer das Ganze, was der RegEx gematcht hat. Hier die ganze Email 'nikic', // Das ist die erste Gruppe (^@]+) (Also der User). 'example.org', // Das ist die zweite Gruppe ((?:[^.]+\.)+[A-Za-z]{2,6}) (Also die Domain). )
Aber das ist nur die einfachste Form von Gruppen. Zum Beispiel gibt es noch die Non-Capturing-Group (?:Ausdruck). Sie erfüllt genau die selbe Funktion wie eine normale Gruppe, dafür wird sie nicht in dieses Ergebnisarray aufgenommen. Wenn du also nicht willst, das etwas in das Ergebnisarray ausgenommen wird, solltest du eine solche Non-Capturing-Group nutzen (weil sie schneller ist.)
Es gibt noch viele, viele anderen Arten von Gruppen. Zum Beispiel (?>, (?|, (?=, (?!, (?<=, (?<!, ..., ..., ..., ..., ... Die werden dir mit der Zeit alle mal unterkommen, aber eben mit der Zeit.
Was bedeutet ?R -> ich vermute mal: Rekursivausführung, d. h.: Wenn (...) vorkommt, soll er für das Innere der Klammern das ganze noch einmal ausführen
Jop, ziemlich genau das. (?R) bedeutet, dass der gesammte RegEx hier nochmal ausgeführt werden soll :)
Es kommt ein Rechenzeichen (wieso "\/"? nach www.sql-und-xml.de verliert / innerhalb [] seine Bedeutung, oder für welches Rechenzeichen steht '\'?)
/ ist der Delimiter. Er muss unter allen Umständen escaped werden ;)
Jetzt zu deinen anderen Fragen:
(?:[0-9]+([.,][0-9]+)?)
Jop. Ich würde aber eine Non-Capturing-Grouuppp nutzen:
(?:[0-9]+(?:[.,][0-9]+)?)
Damit negative Zahlen am Anfang ohne Klammern stehen können bräuchte man dann in Zeile 3:
Zur Zeit werden negative Zahlen weder am Anfang noch sonst wo zugelassen :D (Außer in der Form 0 - 7 für -7 ;)
Daher finde ich deine Ergänzung ziemlich sinnvoll. Aber auch wieder besser mit Non-Capturing Group:
(?:[-+]?[0-9]+(?:[.,][0-9]+)?)
Zusammengesetzt ergibt das also:
/(?:(?:[-+]?[0-9]+(?:[.,][0-9]+)?)|\((?R)\))(?:\s*[-+\/*]\s*(?:(?:[0-9]+(?:[.,][0-9]+)?)|\((?R)\)))*/
Allerdings bin ich mir gerade nicht sicher, ob Zeile 2-6 überhaupt gebraucht werden, wenn Zeile 9-11 hinter Zeile 17 kommen und mit einem '?' versehen werden, - um eine negative Anfangszahl (und auch multi- / dividieren mit negativen Zahlen, ohne Klammersetzung) zu akzeptieren, wird noch eine kleine Veränderung vorgenommen - also:
Das Problem an deinem gekürzten Regex ist, dass er einfach zu frei ist. Es würde auch "3 3 3" matchen oder "3 *". Ich würde daher lieber bei der obigen Variante bleiben. (Am besten mit x-Modifier und Kommentaren, damit mans auch noch versteht...)
Beitrag zuletzt geändert: 18.10.2010 20:13:33 von nikic -
ok,
ich denke jetzt habe ich einiges schon viel besser verstanden...
nur noch zu dem ersten Punkt:
Das wird wohl eher ein Tippfehler sein, du meintest sicher "außer Punkte" ;)
War kein Tippfehler, sondern ein kleiner Denkfehler:
ich hatte nicht bedacht, dass der PUNKT in [] seine Wirkung verliert.
Daher hatte ich an die Wirkung, wie sie hier: http://www.sql-und-xml.de/regex/escapezeichen-klassen-klammern.html beschrieben wird:
Ein spezielles Sonderzeichen stellt der Punkt (.) dar. Dieser erfaßt alle Zeichen mit Ausnahme des Zeilenwechsels \n bzw. #xA.
gedacht ;)
Dass das ?: zu den Klammern gehört, habe ich während ich versucht habe, mich über dieses zu informieren bemerkt, daher habe ich es wohl gegen Ende meines Posts besser unterteilt ;)
lg Ole
EDIT:
weshalb ich so auf dieses Beispiel beharrt habe, liegt daran, dass ich genau dieses in meinem Programm benötige, um damit sicherzustellen, dass mit dem eval()-Befehl wirklich nur eine Rechnung ausgeführt wird
Aber ich wollte halt auch, wie ich im Eröffnungspost gesagt habe, solche Dinge in Zukunft selber lösen können, also noch einmal DANKE für eure Hilfe!
Beitrag zuletzt geändert: 18.10.2010 20:13:29 von ole-reglitzki -
@eval: Achte bei der Prüfung aber unbedingt darauf, dass du Anker benutzt (also ^ und $). Mir passiert es öfters, dass ich die Vergesse und damit das ganze unsicher mache. Also:
if (preg_match('/^(?:(?:[-+]?[0-9]+(?:[.,][0-9]+)?)|\((?R)\))(?:\s*[-+\/*]\s*(?:(?:[0-9]+(?:[.,][0-9]+)?)|\((?R)\)))*$/', $expression)) { $result = eval('return ' . $expression . ';'); }
Was solche Sicherheitsprüfungen im Bezug auf eval angeht, nutze ich gerne auch noch eine andere Technik: Ich prüfe, dass nur erlaubte Tokens genutzt werden ;) Das prüft zwar nicht ob das ganze im Endeffekt Sinn ergibt, aber es prüft, dass nicht gefährliches bei ist. Hier macht der RegEx vielleicht wirklich mehr Sinn, in anderen Fällen aber der Weg über den Tokenizer. Ein Beispiel für das Parsen von Arrays. Bei Mathematischen Expressions wären die erlaubten Tokens dann:
$allowedTokens = array( T_LNUMBER => true, T_DNUMBER => true, T_WHITESPACE => true, ); $allowedChars = array( '+' => true, '-' => true, '*' => true, '/' => true, '(' => true, ')' => true, );
Weiterer Vorteil ist, dass man zum Beispiel T_STRING erlauben könnte, zusammen mit einer Whitelist von bestimmten Funktionen wie sin, cos, pow, sqrt, ...
Beitrag zuletzt geändert: 18.10.2010 20:23:17 von nikic -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage