PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Funktion zur Erkennung von gleichen Zeichen

with 10 comments

Um zu erkennen ob jemand Mist ein ein Formular eingetragen hat brauche (natürlich neben anderen, eventuell wirksameren Maßnahmen) ich ein kleines Script, das erkennt wenn in einem String (Straße, Vorname, Nachname etc.) mindestens 4 mal hintereinander der selbe Buchstabe vorkommt.

Beispiele:
Florian: gültig
qwerty: gültig
asasasasas: gültig
aaaa: ungültig
aaasssdddd: ungültig

Einige Spassvögel tippen einfach Mist in die Formulare, und ein einfacher Check ist: 4 aufeinander folgende Zeichen dürfen nicht gleich sein.

Hier meine erste Lösung:

protected function notCharsXTimesInARow($string, $amount = 4)
{
    $string = strtolower($string);

    if (strlen($string) >= $amount) {
        for ($start=0; $start <= strlen($string)-$amount; $start++) {
            $substring = substr($string, $start, $amount);
            $countChars = count_chars($substring, 1);
            if (count($countChars) == 1) {
                return false;
            }
        }
    }

    return true;
}

Funktioniert wunderbar. Dann habe ich mich an einen Regex gewagt. Mit Backreferences stehe ich eigentlich auf Kriegsfuss, aber damit ist ein Einzeiler möglich:

public function notCharsXTimesInARow($string, $amount = 4)
{
    return !preg_match('/(.)\1{'.($amount-1).',}/', strtolower($string));
}

Bei einer Anzahl von 4 sieht das Pattern dann wie folgt aus:

/(.)\1{3,}/

Das bedeutet so viel wie: Nimm jedes Zeichen, und füge genau dieses Zeichen (Backreference: \1) 3 bis unendlich Mal dahinter. Und gucke dann ob es Matches gibt.

Wie würdet ihr es machen, gibt es noch schönere Lösungen? Mit zwei verschachtelten Schleifen sollte es auch möglich sein. Welche der Lösungen würdet ihr bevorzugen was Wartbarkeit und Lesbarkeit angeht?

Written by Michael Kliewe

November 23rd, 2012 at 8:54 am

Posted in PHP

Tagged with , , ,

10 Responses to 'Funktion zur Erkennung von gleichen Zeichen'

Subscribe to comments with RSS or TrackBack to 'Funktion zur Erkennung von gleichen Zeichen'.

  1. Sieht schon gut aus, allerdings, was ist wenn einer sowas eingibt:

    aaasssd ddd (ein Leerzeichen) ? 🙂

    Macht es Sinn, Leerzeichen zu entfernen?

    return !preg_match('/(.)\1{'.($amount-1).',}/', str_replace(" " , "", strtolower($string)));

    Chris

    23 Nov 12 at 09:08

  2. Klingt jetzt nicht sehr nützlich, weil wenn ich mal so exemplarisch Mist eingebe

    jkkjfddx

    Hab ich nie 4 mal das gleiche Zeichen. Ich denke der Hauptgrund ist, dass bei Gedrückthalten einer Taste das OS immer erstmal eine Pause macht. Somit dauert das einfach so lange 😉 leichter und schneller ist es einmal mit dem Finger, oder der Hand über die Tastatur zu ziehen.

    KingCrunch

    23 Nov 12 at 09:25

  3. @KingCrunch: das ist natürlich richtig, es gibt ja auch noch andere Erkennungsmethoden, häufig wird genacht das gemacht: asdasdasd etc.

    Nur eine der vielen Checks ist die Erkennung von zu vielen gleichen Zeichen hintereinander. Klar dass man damit noch nicht viele erwischt, aber immerhin einige 😉

    Michael Kliewe

    23 Nov 12 at 09:51

  4. Hätte ich auch gesagt,
    ich bekomme viele Kontakanfragen mit „xxxx“ oder „ffff“ ausgefüllten Feldern.
    Gute Idee und vor allem die backreference Geschichte fand ich gut, weil das immer wieder in Vergessenheit gerät. Danke dafür!

    begs

    23 Nov 12 at 11:42

  5. Ich würde alle Buchstaben durchlaufen, mit den letzten merken und mitzählen. Wenn es ein neuer ist einfach von vorne zählen:

    Hier in hübsch: http://pastebin.com/nPCqLpML

    function notCharsXTimesInARow($string, $amount = 4)
    {
    $lastChar = “;
    $counter = 0;
    for ($i = 0; $i < strlen($string); $i++) {
    if ($string[$i] === $lastChar) {
    $counter++;
    } else {
    $counter = 0;
    }

    if ($counter == $amount – 1) {
    return false;
    }
    $lastChar = $string[$i];
    }
    return true;
    }

    $test = array(
    ‚Florian‘ => true,
    ‚qwerty‘ => true,
    ‚asasasasas‘ => true,
    ‚aaaa‘ => false,
    ‚aaasssdddd‘ => false,
    ‚aaasssddd‘ => false
    );

    foreach ($test as $k => $t) {
    if (notCharsXTimesInARow($k) !== $t) {
    echo „$k ist falsch.\n“;
    }
    }

    Fabian Blechschmidt

    23 Nov 12 at 12:18

  6. Sieht doch gut aus.

    Beim Regulären Ausdruck kann man noch ein Zeichen sparen 😉 Das letzte Komma ist hier nicht nötig. /(.)\1{3}/ tut es auch. Mit Komma sucht er nach 3 oder mehr. Ohne Komma hört er einfach auf wenn 3 gefunden.

    Ausserdem könnte man noch statt dem strtolower() beim Regulären Ausdruck einfach ein „i“ (für case insensitive) dran hängen. Etwas eleganter und scheint auch ganz leicht performanter. Dafür etwas unverständlicher.

    Geschmacksache ist dann der Funktionsname bzw. Negierungen im Namen. Meine Präferenz wäre wohl sowas wie: „hasCharRepetition“

    Endergebnis:

    function hasCharRepetition($string, $repetition = 4)
    {
    return preg_match(‚/(.)\1{‚.($repetition-1).‘}/i‘, $string);
    }

    Aber wie man sieht alles eher Besserwisserei aus Spaß an der Freude 😉

    Florian Heinze

    23 Nov 12 at 15:11

  7. Ansonsten, um auch sowas wie adfadsfasdfasdfasdfsadfasdsfasdf zu erkennen, müsste man mit count_chars() doch nettes zaubern können? Das man guckt wie oft kommen Zeichen vor und macht das in Relation zur String-Länge noch Sinn?

    Ein anderer Ansatz mit dem ich mal gearbeitet hatte, war zu berechnen wie aussprechbar das Geschriebene noch ist. Aber das wird spätestens mit Mehrsprachigkeit wohl schwierig oder auch bei Namen.

    Allgemein arbeite ich bei solchen Abwehrmaßnahmen gerne mit einem Scoring. Das ich viele verschieden Filter über verschiedene Felder laufen lasse und eine Gesamtsumme berechne, wie eindeutig die Analysen waren und wie diese gewichtet sind.
    Das gab recht gute Ergebnisse und man nervt die Leute nicht mit Captchas.

    Florian Heinze

    23 Nov 12 at 15:32

  8. Das regexp ist toll aber die Funktion hätte ich wie in einer simplen linearen Datenstruktur gelöst:

    function hasOrderedRepeatsString($string, $times = 2)
    {
    $prev = null;
    for ($i = strlen($string) – 1; $i >= 0; $i–)
    {
    $count = ($string[$i] == $prev)? $count + 1: 1;
    if ($count >= $times) return true;
    $prev = $string[$i];
    }

    return false;
    }

    Aber es geht auch abstrakter, so dass Int, Strings, Arrays und andere Sachen angenommen werden:

    https://gist.github.com/4143270

    So kann z.B. hasOrderedRepeats($_POST, 2) schauen ob zwei mal in Folge das gleiche angegeben wurde. Eventuell könnte da noch was rekursives hinzu.

    Mike Pretzlaw

    25 Nov 12 at 13:19

  9. Ich habe vor geraumer Zeit auch mal einen Blogpost darüber geschrieben: http://www.larsformella.de/2011/06/04/nutzliche-regexes/

    Mein Regex findet auch Sachen wie hihihihi und anderen Crap. Mag vielleicht für den einen oder anderen nützlich sein…

    Lars

    8 Dez 12 at 00:09

  10. Ich habe mich mit dem Thema auch schon auseinander gesetzt. Ist zwar nicht in PHP umgesetzt aber das Script ist nicht sehr komplex daher einfach portierbar.

    http://blog.nager.at/2011/03/text-datenqualitat-messen/

    Tino

    13 Jan 13 at 15:21

Leave a Reply

You can add images to your comment by clicking here.