Christian Seiler: Einen String "entbuchstabisieren"

Beitrag lesen

Hallo Peter,

Hintergrund: Ich habe eine Artikelnummer aus einer externen Quelle, die  einen 7- oder 8-stelligen Mischmasch (z.B.: '54R7A2Z') hat und würde daraus gerne einen reinen Zahlen-String bauen, in dem alle Buchstaben zu 2-stelligen Zahlen gewandelt werden, damit es in unsere WWS-DB passt.

Ich wäre froh über ein paar Lösungsansätze,

Naja, da Du PHP als Thema gewählt hast, gehe ich mal davon aus, dass Du PHP haben willst. Wie andere hier im Thread schon angedeutet haben, ist es nicht ideal, jeden Buchstaben einfach nur in zwei Ziffern zu verwandeln - dann würdest Du Dopplungen kommen, was nicht so toll ist.

Ein brauchbarer Ansatz ist, die Buchstaben/Ziffernkombination der Artikelnummer als Zahl selbst zu betrachten - und zwar im 36er-System, d.h. jede Stelle hat 36 mögliche Werte (10 Ziffern, 26 Buchstaben). Das heißt: eine Ziffer 0 wäre 0 wert, eine Ziffer 1 wäre 1 wert, etc. Ein Buchstabe 'A' wäre 10 wert, ein Buchstabe 'B' 11, eine Buchstabe 'Z' 35. Damit würdest Du z.B. bei einer Zeichenfolge 'A4' folgende Situation haben: Höherwertige Stelle ist 'A', das ist 10 wert, d.h. 10 bei Wertigkeit 1. Niederwertige Stelle ist '4', das ist 4 wert, d.h. 4 bei Wertigkeit 0. Damit hast Du: Zahl = 10 * 36^1 + 4 * 36^0 = 10 * 36 + 4 = 364. Genauso lässt sich jede andere beliebige Zeichenfolge umwandeln.

Du bekommst mit diesem Ansatz jedoch wieder ein Problem: PHP kann zumindest auf 32bit-Systemen nur begrenzt gut mit großen Ganzzahlen umgehen - es gibt irgendwo ein Limit. Bei Deinem Beispiel '54R7A2Z' stößt Du mit meinem vorgeschlagenen System gerade schon ans Limit - da klappt die Umwandlung allerdings selbst noch - die Rückumwandlung versagt bereits. Bei einer Stelle mehr kann es sein, dass sogar die Umwandlung versagt, ein Beispiel für so eine Nummer wäre 'XX4R7A2Z'. Daher musst Du - wenn Du PHP verwenden willst - auf die Funktionen zur Rechnung mit beliebiger Genauigkeit zurückgreifen - die sind etwas langsamer, dafür bekommst Du garantiert eine korrekte Zahl (im Zehnersystem dargestellt).

Beachte jedoch, dass das Problem, das PHP mit den Zahlen hat, unter Umständen auch Deine Datenbank treffen könnte - es hängt davon ab, wie die Spalte dort definiert ist. Mein Beispiel 'XX4R7A2Z' würde nach der oben angegebenen Umwandlungsslogik zur Zahl 2658138786251 verwandelt werden - die hat 12 Stellen! Andererseits ist das von mir vorgestellte Verfahren das kompakteste, das es ermöglicht, die Anforderungen zu erfüllen, sofern eine beliebige (!) Kombination von beliebigen (!) Ziffern und Buchstaben erlaubt ist. Falls es bestimmte Regeln gibt, nach denen sich die bisherige Artikelnummer verhält, dann könnte man das Verfahren optimieren - d.h. wenn man z.B. wüßte, dass die Stellen 1-2 immer Zahlen sind und 3-8 immer Buchstaben (oder so ähnlich), dann könnte man nicht immer fest ins 36er-System umrechnen, sondern in ein gemischtes System, was dann Platz im Zahlenraum sparen würde. Wenn keinerlei Schema für die Artikelnummern bekannt ist, dann muss die Datenbank halt schlicht eine genügend große Spalte haben.

Ich habe Dir mal zwei funktionieren vorprogrammiert, die nach dem von mir beschriebenem Schema arbeiten. Die eine konvertiert Deine Buchstabenfolge in eine Zahl, die andere macht das gleiche wieder rückgängig. Diese Funktionen sollen demonstrieren, wie so ein Problem angegangen werden kann.

<?php  
  
// Konvertiert eine Zeichenkette, die aus Buchstaben A-Z und Ziffern 0-9 besteht  
// in eine Zahl. Dabei wird jedes Zeichen als eine Ziffer im 36er-System betrachtet  
// Die höherwertigste Ziffer kommt zuerst  
function buchstabenZuZahl ($string) {  
    // hilfsvariablen definieren: kleinst- und größtmöglicher buchstabe  
    static $A = 65; // ASCII 'A'  
    static $Z = 90; // ASCII 'Z'  
    // hilfsvariablen: kleinst- und größtmögliche ziffer  
    static $_0 = 48; // ASCII '0'  
    static $_9 = 57; // ASCII '9'  
    // Endergebnis vormerken  
    $resultat = '0';  
    // String in Grossbuchstaben verwandeln  
    // (dann erschlagen wir auch kleinbuchstaben)  
    $string = strtoupper ($string);  
    // bestimmte die länge der zeichenkette  
    $laenge = strlen ($string);  
    // gehe die zeichenkette zeichen für zeichen durch  
    for ($i = 0; $i < $laenge; $i++) {  
        // multipliziere das resultat mit 36, um eine  
        // stelle vorzurücken. Das sollte zwar im ersten  
        // Schleifendurchgang nicht durchgeführt werden,  
        // allerdings ist dort das Resultat bereits 0,  
        // sprich: die Multiplikation ändert nichts  
        $resultat = bcmul ($resultat, 36);  
        // hole das aktuelle zeichen aus der zeichenkette  
        $code = ord ($string[$i]);  
        // haben wir ein ziffer  
        if ($code >= $_0 and $code <= $_9) {  
            // ord ('0') 48, d.h. wenn eine '0' vorkommt,  
            // dann steht hier 48 - 48 = 0, wenn eine '1'  
            // vorkommt, steht hier 49 - 48 = 1, usw. usf.  
            $resultat = bcadd($resultat, $code - $_0);  
        } else if ($code >= $A and $code <= $Z) {  
            // ord ('A') = 65, d.h. wenn ein 'A' vorkommt,  
            // dann steht hier 65 - 65 + 10 = 10, wenn hier  
            // ein Z vorkommt, dann steht hier 90 - 65 + 10 = 35  
            // usw.,usf.  
            $resultat = bcadd ($resultat, $code - $A + 10);  
        } else {  
            // unbekanntes zeichen, ignoriere es  
        }  
    }  
    // gebe das resultat zurück  
    return $resultat;  
}  
  
// Konvertiere eine Zahl an Hand der obigen Regel zurück in eine Zeichenkette  
function zahlZuBuchstaben ($zahl) {  
    // hilfsvariablen definieren: 'A' und '0'  
    static $A = 65; // ASCII 'A'  
    static $_0 = 48; // ASCII '0'  
    // ergebnisstring merken  
    $resultat = '';  
    // nimm den betrag der zahl, sicherheitshalber  
    $zahl = bcmul ($zahl, bccomp ($zahl, 0, 0), 0);  
    // falls die zahl 0 ist, ist die darstellung trivial  
    if (!$zahl) {  
        return '0';  
    }  
    // solange die zahl > 0 ist: füge eine stelle hinzu  
    while ($zahl > 0) {  
        // den 36er-Rest der Zahl holen  
        $rest = bcmod ($zahl, 36);  
        // die Zahl durch 36 Teilen (Integer-Division)  
        // damit bleiben keine Komma-Stellen übrig  
        $zahl = bcdiv ($zahl, 36, 0);  
        // wenn der rest >= 10 ist, wird's ein Buchstabe  
        if ($rest >= 10) {  
            $resultat .= chr ($rest + $A - 10);  
        } else {  
            // es wird ne ziffer  
            $resultat .= chr ($rest + $_0);  
        }  
    }  
    // das problem bleibt jetzt nur, dass $resultat in der  
    // falschen reihenfolge zusammengebaut wurde, daher  
    // muss die zeichenkette noch umgedreht werden  
    return strrev ($resultat);  
}  
  
?>

Viele Grüße,
Christian