PehBehBeh

Erfahrungen eines Hobby-Webentwicklers

PHP-Implementierung der Kölner Phonetik

| 7 Kommentare

Im gestrigen Artikel aus der Serie “Exotische PHP-Funktionen” berichtete ich über zwei Funktionen, mit deren Hilfe sich die Ähnlichkeit zweier Zeichenketten bestimmen lässt. In den Kommentaren wurde bereits erwähnt, dass metaphone() und soundex() nicht optimal für die deutsche Sprache geeignet sind. Deshalb hatte ich sowieso schon geplant, heute die Kölner Phonetik vorzustellen. Dabei handelt es sich um einen Algorithmus, der Wörtern eine Zeichenfolge nach ihrem Klang zuordnet.


Implementierung

Da dieser Algorithmus von Haus aus nicht in PHP implementiert ist, muss dies von Hand geschehen. Hierbei kann man auf die Lösung eines Kommentators im PHP Manual zurückgreifen, der die Kölner Phonetik bereits in eine Funktion verpackt hat.

Der Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php
/**
* A function for retrieving the Kölner Phonetik value of a string
*
* As described at http://de.wikipedia.org/wiki/Kölner_Phonetik
* Based on Hans Joachim Postel: Die Kölner Phonetik.
* Ein Verfahren zur Identifizierung von Personennamen auf der
* Grundlage der Gestaltanalyse.
* in: IBM-Nachrichten, 19. Jahrgang, 1969, S. 925-931
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* @package phonetics
* @version 1.0
* @link http://www.einfachmarke.de
* @license GPL 3.0 <http://www.gnu.org/licenses/>
* @copyright  2008 by einfachmarke.de
* @author Nicolas Zimmer <nicolas dot zimmer at einfachmarke.de>
*/
 
function cologne_phon($word) {
    /**
     * @param  string  $word string to be analyzed
     * @return string  $value represents the Kölner Phonetik value
     * @access public
     */
    
    // prepare for processing
    $word = strtolower($word);
    $substitution = array(
        "ä"=>"a",
        "ö"=>"o",
        "ü"=>"u",
        "ß"=>"ss",
        "ph"=>"f"
    );
 
    foreach ($substitution as $letter => $substitution) {
        $word = str_replace($letter,$substitution,$word);
    }
 
    $len = strlen($word);
 
    // Rule for exeptions
    $exceptionsLeading = array(
        4 => array("ca","ch","ck","cl","co","cq","cu","cx"),
        8 => array("dc","ds","dz","tc","ts","tz")
    );
 
    $exceptionsFollowing = array("sc","zc","cx","kx","qx");
 
    //Table for coding
    $codingTable = array(
        0  => array("a", "e", "i", "j", "o", "u", "y"),
        1  => array("b", "p"),
        2  => array("d", "t"),
        3  => array("f", "v", "w"),
        4  => array("c", "g", "k", "q"),
        48 => array("x"),
        5  => array("l"),
        6  => array("m", "n"),
        7  => array("r"),
        8  => array("c", "s", "z"),
    );
 
    for ($i=0; $i<$len; $i++) {
        $value[$i] = "";
 
        //Exceptions
        if ($i == 0 && $word[$i].$word[$i+1] == "cr") {
            $value[$i] = 4;
        }
 
        foreach ($exceptionsLeading as $code => $letters) {
            if (in_array($word[$i].$word[$i+1], $letters)) {
                $value[$i] = $code;
            }
        }
 
        if ($i != 0 && (in_array($word[$i-1].$word[$i], $exceptionsFollowing))) {
            $value[$i] = 8;
        }
 
        // normal encoding
        if ($value[$i] == "") {
            foreach ($codingTable as $code => $letters) {
                if (in_array($word[$i], $letters)) {
                    $value[$i] = $code;
                }
            }
        }
    }
 
    // delete double values
    $len = count($value);
 
    for ($i=1; $i<$len; $i++) {
        if ($value[$i] == $value[$i-1]) {
            $value[$i] = "";
        }
    }
 
    // delete vocals
    for ($i=1; $i>$len; $i++) {
        // omitting first characer code and h
        if ($value[$i] == 0) {
            $value[$i] = "";
        }
    }
    
    $value = array_filter($value);
    $value = implode("", $value);
 
    return $value;
}

Beispiel

Hier noch ein paar Beispiele:

1
2
3
4
echo cologne_phon("Hans"); // 68
echo cologne_phon("Franz"); // 3768
echo cologne_phon("Schokolade"); // 8452
echo cologne_phon("Raddampfer"); // 726137
Dir hat der Artikel gefallen?
Dann abonniere doch den RSS-Feed!
  • Daniel

    Klasse, vielen Dank für diesen interessanten Artikel. Hatte mich vorher noch nicht mit der Ähnlichkeit von Wörtern beschäftigen müssen. Um so interessanter zu sehen, dass es Algorithmen dafür gibt die so in PHP implementiert werden können. Das Beispiel ist auch aussagekräftig, danke!

  • Hi,

    vielen Dank für den Code, kann ich gut gebrauchen! Ich habe bei mir PHP streng eingestellt und es kommt bei mir in der Originalversion ein Fehler, weil über das Wortende hinaus gelesen wird. Ich habe deshalb in den Zeilen 72-83 ein paar kleine Änderungen vorgenommen:

    //Exceptions
    if ($i == 0 && $i<$len-1 && $word[$i].$word[$i+1] == "cr") {
    $value[$i] = 4;
    }

    if($i<$len-1) {
    foreach ($exceptionsLeading as $code => $letters) {
    if (in_array($word[$i].$word[$i+1], $letters)) {
    $value[$i] = $code;
    }
    }
    }

    Beste Grüße,
    Andre

    PS: Repost mit htmlencoded entities, da beim vorigen Eintrag ein Teil vom Code geschluckt wurde

  • Das Beispiel für Hans scheint mir nicht korrekt zu sein. 0 am Anfang bleibt laut wikipedia stehen, sollte also 068 sein. Ich schreibe gerade ein paar unit tests für diesen Algorithmus und werde mir demnächst wohl mal die Primärquelle anschauen.

  • Zunächst erstmal Danke den Artikel!!

    Hier zwei kleine Fehler, die in PHP 5.3.2 auftreten:

    Zeile 107 sollte lauten (siehe kleiner Zeichen ist umgedreht): for ($i=1; $i<$len; $i++) {

    Zeile 114 habe ich komplett auskommentiert, da dies auch 0'en entfernt, das wollen wir doch nicht 🙂

    Danke nochmals,

    Liebe Grüße

    markus

  • Habe gerade keine Zeit das zu überprüfen, aber vielen Dank für den Hinweis!

  • Krauß

    Hallo !

    Ich bin soeben über den Artikel der
    ‚PHP-Implementierung der Kölner Phonetik‘ gestolpert.

    Wenn ich in die angegebene Funktion den Begriff ‚Schloßstraße‘ eingbe,
    dann erhalte ich eine Notiz:

    Notice: Uninitialized string offset: 14 in […][…] on line 55

    Und als Ergebnis:

    8588278

    Ist es dann richtig wenn ich als SQL in der MySQL folgende Query absetze:

    select * from foo soundex(‚8588278‘);

    Ich würde mich über eine Information freuen.

    Mit freundlichen Grüßen

    Stephan

  • @Stephan:
    Das „Problem“ mit der Notice „Uninitialized string offset“ hatte ich auch…
    Behoben habe ich das, indem ich Zeile 77wie folgt abgeändert habe:

    Original:
    if (in_array($word[$i].$word[$i+1], $letters)) {

    Geändert:
    if (isset($word[$i+1]) && in_array($word[$i].$word[$i+1], $letters)) {

    Hoffe es hilft weiter…

    Grüße,
    Samir