php string auf wenigstens 3 Buchstaben prüfen
Sven
- php
Hallo,
ich möchte in php einen String daraufhin prüfen, ob er wenigstens 3 Buchstaben (groß oder kleingeschrieben ist egal) enthält.
Kann ich das nur mit einem regulären Ausdruck machen oder geht das auch ohne?
Sven
ich möchte in php einen String daraufhin prüfen, ob er wenigstens 3 Buchstaben (groß oder kleingeschrieben ist egal) enthält.
Kann ich das nur mit einem regulären Ausdruck machen oder geht das auch ohne?
Guckst Du Handbuch:
https://www.php.net/manual/en/function.strlen.php
Falls Dein String UTF-8-kodiert ist oder sein kann:
https://www.php.net/manual/en/function.mb-strlen.php
Falls Du jetzt bei der Zählung Leerzeichen oder andere unerwünschte Zeichen („Nicht-Buchstaben“ und Ziffern) ausschließen willst, schau Dir
https://www.php.net/manual/en/function.preg-replace.php
und die Filter
https://www.php.net/manual/en/book.filter.php
an.
Hi,
ich möchte in php einen String daraufhin prüfen, ob er wenigstens 3 Buchstaben (groß oder kleingeschrieben ist egal) enthält.
Kann ich das nur mit einem regulären Ausdruck machen oder geht das auch ohne?
ich wäre nicht auf die Idee gekommen, das mit einem RegEx zu machen. Stattdessen würde ich zeichenweise durch den String laufen und die Zeichen zählen, die die Bedingung erfüllen.
Und die Bedingung ist ungenau formuliert: Was ist nach deiner Definition ein Buchstabe? Ist φ einer? Für mich schon. Ist é einer? Selbstverständlich.
Mit anderen Worten: Meinst du nur Buchstaben des lateinischen Alphabets oder willst du den Begriff allgemeiner fassen?
Einen schönen Tag noch
Martin
Um mal die von Dir zu treffenden Definitionen zu verdeutlichen:
Erst einmal willst Du ja nicht nur die Länge der Eingabe wissen sondern diese wahrscheinlich weiterverarbeiten…
<?php
$inputs = [
' gjf5asfd98?(fg)',
'Jörg',
'123Jö'
];
## Nur Buchstaben aus ASCII. (Keine Umlaute...)
$filter = '[^a-zA-z]';
foreach ( $inputs as $input ) {
$filtered = mb_ereg_replace( $filter, '', $input );
echo "String: $input\n";
echo "Gefiltert: $filtered\n";
echo mb_strlen( $filtered ) . " Zeichen.\n\n";
}
Mit dieser Definition von “nur Buchstaben“ hast Du dann folgende Ergebnisse:
String: gjf5asfd98?(fg)
Gefiltert: gjfasfdfg
9 Zeichen.
String: Jörg
Gefiltert: Jrg
3 Zeichen.
String: 123Jö
Gefiltert: J
1 Zeichen.
Hallo Sven,
wenn ich Dich wörtlich nehme, d.h. Du willst nur prüfen, OB drei Buchstaben drin sind, könnte man ganz naiv so testen:
if (preg_match("/([a-z].*){3,}/i", $text)) {
echo "Mindestens 3 Buchstaben gefunden";
}
Die Angabe {3,}
besagt: das Element links von mir muss mindestens dreimal vorkommen.
Das i am Ende des Patterns heißt "case insensitive", dadurch kommen außer a-z auch A-Z mit.
Natürlich beginnt nun die Überlegung, was ein "Buchstabe" ist. a.z und A-Z ist die ASCII Antwort, bereits die deutsche Antwort ist anders und wenn es international wird, beginnt das Drama.
Die Regex-Characterklasse \w hilft Dir nichts, weil sie auch Ziffern beinhaltet.
Wenn Du nur die Buchstaben der deutschen Sprache finden willst, könntest Du verleitet sein, für ein Zeichen [a-zA-ZäöüÄÖÜß]
hinzuschreiben, und fällst auf die Nase bei Fremdwörtern wie Café oder einer Schreibweise wie GROẞHANDEL (da ist das ß in seiner Großschriftvariante ẞ drin).
Die universellste Alternative wäre die Verwendung von \pL - das wäre die Unicode-Zeichenkategorie "Letter" in all ihren Variationen. Das könnte dann aber wieder zu viel sein, denn ein "Letter" im Unicode sind auch kyrillische oder thailändische Buchstaben.
Also: Um deine Frage zu beantworten, müsstest Du erstmal festlegen, was für deinen Anwendungsfall ein "Buchstabe" sein soll.
Die Frage ist dann auch, ob Du die Positionen der gefundenen Buchstaben brauchst, und ob es wichtig ist, ob sie beieinander stehen.
Rolf
Hallo Rolf,
wenn ich Dich wörtlich nehme, d.h. Du willst nur prüfen, OB drei Buchstaben drin sind, könnte man ganz naiv so testen:
if (preg_match("/([a-z].*){3,}/i", $text)) { echo "Mindestens 3 Buchstaben gefunden"; }
das stimmt nicht ganz: Dieser Ausdruck sagt dir, ob irgendwo drei oder mehr Buchstaben unmittelbar hintereinander auftreten. Aber drei Buchstaben wahllos in einer Wüste von Ziffern verteilt findet er nicht.
Einen schönen Tag noch
Martin
Hallo Der Martin,
Dieser Ausdruck sagt dir, ob irgendwo drei oder mehr Buchstaben unmittelbar hintereinander auftreten.
Rolf
Hallo Rolf,
Dieser Ausdruck sagt dir, ob irgendwo drei oder mehr Buchstaben unmittelbar hintereinander auftreten.
magst du mir erklären, warum?
Ich dachte immer, der Quantifier {3,} bedeute: Mindestens 3 direkt aufeinanderfolgende Vorkommen des vorangehenden Elements.
Warum ist das hier nicht so?
Einen schönen Tag noch
Martin
Ich dachte immer, der Quantifier {3,} bedeute: Mindestens 3 direkt aufeinanderfolgende Vorkommen des vorangehenden Elements.
Warum ist das hier nicht so?
Wir reden über:
"/([a-z].*){3,}/i"
Es ist auch hier so. Aber das dem Quantifier {3,}
„vorangehende Element“ ist ([a-z].*)
, mithin ein Zeichen aus den Buchstaben a-z
oder A-Z
(wg. /i
), gefolgt von einem, keinem oder mehreren beliebigen Zeichen. Dein Irrtum betrifft also das Wesen des „Elements“: Ein „Element“ kann ein, mehrere oder sogar kein Zeichen enthalten. Notiert wird es also vorliegend als geklammerter Ausdruck.
Hallo,
"/([a-z].*){3,}/i"
Es ist auch hier so. Aber das dem Quantifier
{3,}
„vorangehende Element“ ist([a-z].*)
, mithin ein Zeichen aus den Buchstabena-z
oderA-Z
(wg./i
), gefolgt von einem, keinem oder mehreren beliebigen Zeichen.
ja, danke. Ich hatte Punkt-Stern übersehen. Scheuklappen oder so. Oder Tunnelblick.
Dein Irrtum betrifft also das Wesen des „Elements“: Ein „Element“ kann ein, mehrere oder sogar kein Zeichen enthalten. Notiert wird es also vorliegend als geklammerter Ausdruck.
Jetzt ist es mir auch klar.
Einen schönen Tag noch
Martin
Hallo Der Martin,
Ich dachte immer, der Quantifier {3,} bedeute: Mindestens 3 direkt aufeinanderfolgende Vorkommen des vorangehenden Elements
Genau.
Warum ist das hier nicht so?
Warum glaubst Du, dass das hier nicht so wäre?
Finde das "vorangehende Element" von {3,}. Klammere Dich dabei nicht zu sehr an einzelnen Zeichen fest…
Edith meint: dammit, fastix was faster than me and let the cat out of the bag
Rolf
Edith meint: dammit, fastix was faster than me and let the cat out of the bag
Dafür hat er mehrere Anläufe (und also länger) gebraucht, bis er mit seiner Antwort halbwegs zufrieden war...
Um die Anzahl von Übereinstimmungen zu ermitteln böte sich preg_match_all
an:
Rückgabewerte: Gibt die Anzahl der Übereinstimmungen mit dem kompletten Suchmuster zurück (die auch Null sein kann). Bei einem Fehler wird false zurückgegeben...
z.B. mit \pL
für UTF-8 oder eben [A-Za-z]
für ASCII letters wie von Rolf beschrieben.
if(preg_match_all('~\pL~u', $str) >= 3)
{
// tu was
}
Siehe Beispiel bei Tio.run (Try it Online).
Bezüglich \pL
für die Unicode Kategorie hier noch der Hinweis, dass dies eine PCRE-spezifische (ev. auch ECMAScript>=2018) Kurzschreibweise für \p{L}
ist, welche ebenso in anderen Regex flavors, wie z.B. #C/.NET oder JAVA zur Verfügung steht. Letzteres ist jedenfalls mehr kompatibel.
PS: Bei UTF-8 empfiehlt es sich, das u
(PCRE_UTF8) flag für die preg* Funktionen zu verwenden.
Hello,
ich möchte in php einen String daraufhin prüfen, ob er wenigstens 3 Buchstaben (groß oder kleingeschrieben ist egal) enthält.
Kann ich das nur mit einem regulären Ausdruck machen oder geht das auch ohne?
Dafür muss man den erlaubten Zeichenvorrat kennen und dessen Codierung (Singlebyte, Multibyte, ...).
Wie lang darf der gesamte String in Zeichen werden? Wenn das egal ist, könntest Du den String von vorne bis hinten durchgehen per Schleife und prüfen, ob das Zeichen an der aktuellen Position im Set der erlaubten Zeichen vorhanden ist.
Das geht bei ASCII-Zeichen auch mittels zwei Range-Tests über die Ordnungszahl, denn die ASCII-Zeichen (Singlebyte) liegen in der Kodierung alle hintereinander. Damit erspart man sich den zweiten Stringvergleich.
Diese Vorgehensweise ist in PHP übrigens nicht unbedingt langsamer, als die Regular Expressions zu bemühen. Die machen im Prinzip im Hintergrund nichts anderes, nur noch sehr viel mehr.
Glück Auf
Tom vom Berg
Diese Vorgehensweise ist in PHP übrigens nicht unbedingt langsamer
Hallo Tom vom Berg,
Unbedingt ist die langsamer und mehr Kot ist es auch noch :-D
Regexes per se sind nicht langsam, sie werden nur gern falsch verwendet.
Mag schon sein, dass es sich ohne noch irgendwie etwas performanter lösen lässt, aber imho handelt es sich hier um eine klassische Aufgabe für Regex.
Hello,
Diese Vorgehensweise ist in PHP übrigens nicht unbedingt langsamer
Hallo Tom vom Berg,
I Pfui. Sowas ist natürlich kontraproduktiv:
$j < strlen($str)
Die Stringlänge prüft man vorher einmalig.
Oder ändert die sich während der Prüfung dynamisch? ;-P
Das ändert aber nicht viel an der Gesamtaussage. Wenn man die RegEx-Maschine einmalig für 1.000.000 Durchläufe instantiiert, gewinnt die selbstverständlich.
Wenn man die aber in jedem Einzelrequest erst instantiieren muss, sieht der Vergleich schon anders aus. Man sollte also immer bedenken, ob man das gute Teil innerhalb eines Requests schon benutzt hat, oder eben nicht.
Glück Auf
Tom vom Berg
Hi Tom,
Sowas ist natürlich kontraproduktiv
Es ändert sich da nicht viel...
Das ändert aber nicht viel an der Gesamtaussage. Wenn man die RegEx-Maschine einmalig für 1.000.000 Durchläufe instantiiert, gewinnt die selbstverständlich.
Kann ich nicht nachvollziehen.
Man kann sehr viel darüber reden, es wird aber dabei bleiben, dass eine Regex-Lösung hier elegant, zweckmäßig und performant ist.
Hello,
Sowas ist natürlich kontraproduktiv
Es ändert sich da nicht viel, habs nur eben zur Illustration hingerotzt.
Habe ich selbstverständlich auch ausprobiert. Deinen Hinweis habe ich durchaus ernst genommen.
Deshalb:
Das ändert aber nicht viel an der Gesamtaussage. Wenn man die RegEx-Maschine einmalig für 1.000.000 Durchläufe instantiiert, gewinnt die selbstverständlich.
Kann ich nicht nachvollziehen.
Ist auch schwierig, den Aufwand der RexEx-Maschine zu ermitteln.
Man kann sehr viel darüber reden, es wird aber dabei bleiben, dass eine Regex-Lösung hier elegant, zweckmäßig und performant ist.
Ich habe doch gar nichts dagegen.
Insbesondere, bei 1.000.000 Anwendungen innerhalb eines Scripts. ;-P
Glück Auf
Tom vom Berg
Wenn sich Rolf mehr Mühe mit dem Suchmuster gegeben hätte, gewänne seine Variante:
preg_match("/(?:[a-z][^a-z]*){2}[a-z]/i", $str)
Ist auch hübsch, allerdings etwas schwieriger zu lesen wenn man wenig mit Regex macht.
Hallo Jonny 5,
danke für den Benchmark.
Meine Variante hatte vor allem ein Ziel: Das Pattern für den "Buchstaben" nur einmal in der Regex haben zu müssen. Denn je nachdem, was Du als "Buchstabe" definierst, kann das umfangreicher werden.
Nachteilig ist dabei, dass die Regex-Engine wegen des .* ins Backtracking kommt, d.h. nach jedem .* matcht sie bis zum String-Ende, merkt, dass im Pattern noch etwas kommt und setzt zurück, um zu schauen, ob sie dafür einen Match findet. Das kann man im Regex-Debugger von regex101.com gut feststellen. Dein [^a-z] verhindert das Backtracking und macht die Regexp damit schneller.
Aber das macht dann 0.3 Sekunden bei einer Million Durchläufen aus. Ja, die Laufzeit halbiert sich dadurch. Aber wir reden hier von 300ns Laufzeitdifferenz.
Man muss aufpassen, dass man sich in der Mikrooptimierung nicht verliert. Diese 300ns bezahlt Dir normalerweise niemand. Bei einer Seite wie Facebook, die zumindest früher einmal unter PHP lief (wenn auch nicht unter ZEND, sondern mit einer selbstgeschriebenen Engine), bei der diese 300ns tatsächlich eine Million mal pro Sekunden fällig werden könnten, sieht das anders aus. Da können schon minimale Unterschiede einen Unterschied von 2-3 Servern im Rechenzentrum bedeuten.
Rolf
Hallo Rolf!
Noch als kleine Nörgelei bezüglich deines Suchmusters: Es würde z.B. keine 3 Buchstaben in
"ab\nc"
erkennen aufgrund des fehlenden single line flags. Nur um es kurz zu erwähnen.
Wenn es um eine Passwortgeschichte geht, wird das keine große Rolle spielen, da wohl kaum jemand einen Linebreak in ein entsprechendes Feld eingeben würde. Geht es jedoch um andere Strings wo man kontrollieren möchte, ob 3 Buchstaben enthalten sind dann ist es schon wichtig.
Hallo Jonny 5,
da wohl kaum jemand einen Linebreak in ein entsprechendes Feld eingeben würde
das kann man in einem Feld input type="passwort" meines Wissens auch gar nicht.
Die mögliche Anforderung, zeilenübergreifend zu suchen, hatte ich nicht bedacht, stimmt. Meine Telepathiemütze scheint einen Defekt zu haben, und die Self-Glaskugels bestehen ja bekanntlich aus Milchglas. Sofern sie nicht am Christbaum hängen, dumme Aufkleber tragen oder kaputt sind… 😉
Rolf
Hi Rolf. Sehr nette, umfassende und unterhaltsame Glaskugelsammlung! Danke dafür.
ich möchte in php einen String daraufhin prüfen, ob er wenigstens 3 Buchstaben ...enthält
Ohne mehr oder weniger transparente oder beschädigte Exemplare bemühen zu müssen ist dies die Annahme, welche das Vorhandensein von Linebreaks grundsätzlich nicht ausschließt.
Meine Telepathiemütze scheint einen Defekt zu haben
Ich hoffe du trägst keinen Aluhut darüber, dann wirken die angeblich nicht mehr so gut. :-D
Hello,
Wenn es um eine Passwortgeschichte geht, wird das keine große Rolle spielen, da wohl kaum jemand einen Linebreak in ein entsprechendes Feld eingeben würde. Geht es jedoch um andere Strings wo man kontrollieren möchte, ob 3 Buchstaben enthalten sind dann ist es schon wichtig.
Und wenn das doch jemand tut, muss er/sie wohl am Formular vorbei gepostet haben. Dann liegt womöglich ein Angriffsversuch vor, zumindest ein schwerer Fehler.
Da wären wir wieder beim Thema Sicherheit und den Plausibilitätsprüfungen, die eigentlich wichtiger sind, als die Benutzergängeleien.
Glück Auf
Tom vom Berg