Gefällt dir dieser Artikel?

PHP/Regex: String MUSS Zahlen UND Buchstaben enthalten

erschienen in der Kategorie Software, am 09.03.2015
Schnatterente
Ich wurde kürzlich beim Entwickeln eines PHP-Skripts mit dem Problem konfrontiert, dass ich ein Regex-Pattern brauchte, das auf Zeichenketten (Strings) anspringt, welche aus Zahlen sowie kleinen und großen Buchstaben bestehen. Hierbei galt jedoch die Bedingung, dass in den jeweiligen Strings Zahlen UND Buchstaben enthalten sein mussten. Ein einfaches "[A-z0-9]+"-Pattern reichte dafür also nicht aus.

Nach einigen Minuten der Tüftelei und Ausprobiererei hatte ich ein schönes Pattern gefunden und einen kleinen PHP-Code geschrieben, der so ziemlich genau das Ergebnis lieferte, das ich für meine Arbeit brauchte. Da ich mir vorstellen kann, dass das noch für andere Leute oder Projekte nützlich sein könnte, stelle ich den Code hier online und zeige kurz, wie man ihn benutzen kann.
Das Regex-Pattern könnte (mit kleineren Anpassungen) beispielsweise dafür genutzt werden, zufällige Tokens oder Hashwerte zu erkennen, oder zu überprüfen, ob ein Passwort Zahlen und Buchstaben enthält.
In Sachen PHP will ich kurz ein paar Worte zur Verwendung der preg_match_all()-Funktion verlieren.

Regex: String MUSS Zahlen UND Buchstaben enthalten

Das Ziel soll es also sein, jene alphanumerischen Substrings aus einem langen String zu extrahieren, die sowohl Zahlen als auch Buchstaben enthalten. Eine solche Ausgangs-Zeichenkette könnte beispielsweise so aussehen:
aSdF1234-1234aSdF-AdSf123asdf-1234AsDf1234-a1S2d3F4-1a2s3d4f-asdf-1234

Gelb hervor gehoben sind hierbei genau die Substrings, die im Ergebnis enthalten sein sollen. (Die letzten beiden Varianten fallen raus, weil sie nur Zahlen ODER Buchstaben enthalten.)

Das entstandene, zu dieser Anforderung passende, Regex-Pattern sieht so aus:
/([A-Za-z]+(\d+[A-Za-z]*)+|[A-Za-z]*\d+[A-Za-z]+)[A-Za-z0-9]*/

Es garantiert, dass in der Zeichenfolge Zahlen und Buchstaben enthalten sind, wobei deren Reihenfolge vollkommen egal ist. (Es gibt auch andere Varianten das zu lösen, z.B. mit Look-Ahead-Assertions, aber die hier fand ich recht schön und simpel.)

PHP: Finden aller Matches mit preg_match_all()

Um die oben gezeigten Substrings mit dem Regex-Pattern zu extrahieren, kann die PHP-Funktion preg_match_all() genutzt werden, die hier ausführlich beschrieben wird.
Sie durchsucht einen übergebenen String nach Übereinstimmungen mit einem regulären Ausdruck und schreibt alle Ergebnisse (insofern dies gewünscht wird) in ein Array. Als Rückgabewert liefert die Funktion die Anzahl der gefundenen Matches.
Der Aufbau des (mehrdimensionalen) Ergebnis-Arrays hängt von den gesetzten Flags ab (PREG_PATTERN_ORDER, PREG_SET_ORDER oder PREG_OFFSET_CAPTURE). Was genau die drei Flags bewirken, ist im verlinkten PHP-Manual recht gut beschrieben. Um das Verständnis zu erleichtern, zeige ich hier aber einfach mal die Ergebnismengen für den oben genannten Beispiel-String in allen drei Varianten:
PREG_PATTERN_ORDER, PREG_SET_ORDER und PREG_OFFSET_CAPTURE

Die (bei PREG_PATTERN_ORDER und PREG_OFFSET_CAPTURE äußeren bzw. bei PREG_SET_ORDER inneren) Arrays mit den Indizes 0 und 1 und entstehen durch den Aufbau des Regex-Patterns.
Die Sortierung von PREG_OFFSET_CAPTURE entspricht der Sortierung von PREG_PATTERN_ORDER, wobei hier zusätzlich (wie der Name schon sagt) das Offset (also die Position) des jeweiligen Substrings im durchsuchten String mit ausgegeben wird. (Was in vielen Fällen sehr praktisch sein kann, vor allem wenn man vorhat, den String noch weiter zu bearbeiten.)

Wie man gut erkennen kann (und das war auch zu erwarten), liefert der verwendete Ausdruck zum Teil doppelte Matches zurück, bzw. Substrings von anderen Ergebnisstrings.
Die Verwendung der PREG_OFFSET_CAPTURE-Flag bietet sich in solchen Fällen an, da man hier leicht die zusammengehörigen (doppelten) Strings erkennen und sie z.B. auf Basis der strlen()-Funktion aussortieren kann.
Im vorliegenden Fall wird aber einfach immer zum nullten Array-Element gegriffen, da dieses stets die vollständigere Variante des Substrings enthält.

Unter Berücksichtigung all dieser Überlegungen ergibt sich der folgende, recht kurze, aber wirkungsvolle PHP-Code, der exakt die oben gelb markierten Substrings ausgibt und in ein Array ($interestingStrings) schreibt:
$strings = array();
preg_match_all("/([A-Za-z]+(\d+[A-Za-z]*)+|[A-Za-z]*\d+[A-Za-z]+)[A-Za-z0-9]*/",
   $inputString, $strings, PREG_SET_ORDER);
$interestingStrings = array();
for ($i=0; $i<count($strings);$i++){
   echo $strings[$i][0]."<br/>";
   array_push($interestingStrings,$strings[$i][0]);
}

Ergebnis:
asdf1234
1234asdf
adsf123asdf
1234asdf1234
a1s2d3f4
1a2s3d4f


Mission accomplished. :-)

Das wär's dann auch schon dazu. Zum Schluss habe ich noch einen Tipp, was das Basteln von regulären Ausdrücken angeht. Schaut euch mal diese, aus meiner Sicht sehr hilfreichen, Webseiten an:
  • regex101.com: Ein sehr umfangreiches Entwicklungstool für reguläre Ausdrücke. (Wenn man ein Match-All-Verhalten will, muss man den Modifier "g" angeben!)
  • phpliveregex.com: Auch schön, hier kann man direkt zwischen verschiedenen PHP-Funktionen umschalten.
  • RegexPlanet: Hat ein paar Funktionen weniger, ist dafür aber schön übersichtlich.
  • functions-online.com: Damit kann man PHP-Funktionen direkt online testen (und im Falle von preg_match_all() auch die Flags definieren).
Es gibt noch viele weitere derartige Seiten. Google ist dein Freund. ;-)

Geschnatter

2 Kommentare, selbst mitschnattern << < Seite 1/1 > >>
claw, am 10.03.2015 um 10:18 Uhr
Ruby:
http://rubular.com/r/ZeC1lOIhZK
Rumpelwicht, am 10.03.2015 um 14:20 Uhr
Wenn's mal schnell gehen muß :)

http://www.regexr.com/