Phonex

Soyez le premier à donner votre avis sur cette source.

Vue 15 852 fois - Téléchargée 619 fois

Description

Tout comme soundex2, ceci est une adaptation php de l'algo phonex qui a été créé, et décrit ici : http://sqlpro.developpez.com/cours/soundex/ par Frédéric BROUARD.

PRECISIONS SUR L'UTILITE :
Ces algo, soundex, soundex2, phonex, metaphone, assigne un code à une chaîne donnée. Ce code est calculé en fonction de la phonétique, donc de la prononciation de cette chaîne.
En l'occurence, les 2 algo présents par défaut dans php, soundex () et metaphone () ne prennent en compte que la prononciation anglaise.
Celui-ci, basé sur phonex (un algo plus performant que soundex ou soundex2), est francisé.

Evidemment, 2 chaînes différentes peuvent avoir le même code. Par exemple, ici, 'gros' aura le même code phonex que 'grau'.
Ce qui veut dire, dans le cadre d'une recherche sur une base de données contenant des noms, par exemple, on peut effecyuer une recherche phonétique aussi! Bref, le mec a parlé avec un cilent par téléphone, mais il ne s'est pas comment s'écrit exactement son nom de famille...gros, graus, grau, graux, greaux...? etc... Il tape par exemple gros, et effectue une recherche phonex. Cette recherche lui ressortira tous les noms dont le code phonex est le même que 'gros'. Donc si le client s'appelait 'Graux', il le trouvera.

Couplé à l'algo de levenshtein (fonction interne php) en plus, on peut avoir une recherche phonétique par pertinence...les codes identiques en premiers, puis ceux un peu différents, etc...jusqu'à un degré de différence voulu.

Le mieux, dans le cadre d'une bdd, est évidemment de stocker le code phonex dans la base, histoire de ne pas le recalculer à chaque recherche.

Source / Exemple :


<?php
/**


*

  • author Johan Barbier <barbier_johan@hotmail.com>
  • /
class phonex { /**
  • The public string we will work on
  • /
public $sString = ''; /**
  • private replacement array
  • /
private $aReplaceGrp1 = array ( 'gan' => 'kan', 'gam' => 'kam', 'gain' => 'kain', 'gaim' => 'kaim' ); /**
  • private replacement array
  • /
private $aReplaceGrp2 = array ( '/(ain)([aeiou])/' => 'yn$2', '/(ein)([aeiou])/'=> 'yn$2', '/(aim)([aeiou])/' => 'yn$2', '/(eim)([aeiou])/'=> 'yn$2', ); /**
  • private replacement array
  • /
private $aReplaceGrp3 = array ( 'eau' => 'o', 'oua' => '2', 'ein' => '4', 'ain' => '4', 'eim' => '4', 'aim' => '4' ); /**
  • private replacement array
  • /
private $aReplaceGrp4 = array ( 'é' => 'y', 'è' => 'y', 'ê' => 'y', 'ai' => 'y', 'ei' => 'y', 'er' => 'yr', 'ess' => 'yss', 'et' => 'yt' ); /**
  • private replacement array
  • /
private $aReplaceGrp5 = array ( '/(an)($|[^aeiou1234])/' => '1$2', '/(am)($|[^aeiou1234])/' => '1$2', '/(en)($|[^aeiou1234])/' => '1$2', '/(em)($|[^aeiou1234])/' => '1$2', '/(in)($|[^aeiou1234])/' => '4$2' ); /**
  • private replacement array
  • /
private $aReplaceGrp6 = array ( '/([aeiou1234])(s)([aeiou1234])/' => '$1z$3' ); /**
  • private replacement array
  • /
private $aReplaceGrp7 = array ( 'oe' => 'e', 'eu' => 'e', 'au' => 'o', 'oi' => '2', 'oy' => '2', 'ou' => '3' ); /**
  • private replacement array
  • /
private $aReplaceGrp8 = array ( 'ch' => '5', 'sch' => '5', 'sh' => '5', 'ss' => 's', 'sc' => 's' ); /**
  • private replacement array
  • /
private $aReplaceGrp9 = array ( '/(c)([ei])/' => 's$2' ); /**
  • private replacement array
  • /
private $aReplaceGrp10 = array ( 'c' => 'k', 'q' => 'k', 'qu' => 'k', 'gu' => 'k', 'ga' => 'ka', 'go' => 'ko', 'gy' => 'ky' ); /**
  • private replacement array
  • /
private $aReplaceGrp11 = array ( 'a' => 'o', 'd' => 't', 'p' => 't', 'j' => 'g', 'b' => 'f', 'v' => 'f', 'm' => 'n' ); /**
  • private replacement array
  • /
private static $aReplaceGrp12 = array ( '1', '2', '3', '4', '5', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'n', 'o', 'r', 's', 't', 'u', 'w', 'x', 'y', 'z' ); /**
  • private replacement array
  • /
private $aEnd = array ( 't', 'x' ); /**
  • public function build ()
  • main method, building the phonex code of a given string
  • @Param string sString : the string!
  • /
public function build ($sString) { if (is_string ($sString)) { $this -> sString = $sString; } $this -> sString = strtolower ($this -> sString); $this -> sString = str_replace ('y', 'i', $this -> sString); $this -> sString = preg_replace ('/(?<![csp])h/', '', $this -> sString); $this -> sString = str_replace ('ph', 'f', $this -> sString); $this -> aReplace ($this -> aReplaceGrp1); $this -> aReplace ($this -> aReplaceGrp2, true); $this -> aReplace ($this -> aReplaceGrp3); $this -> aReplace ($this -> aReplaceGrp4); $this -> aReplace ($this -> aReplaceGrp5, true); $this -> aReplace ($this -> aReplaceGrp6, true); $this -> aReplace ($this -> aReplaceGrp7); $this -> aReplace ($this -> aReplaceGrp8); $this -> aReplace ($this -> aReplaceGrp9, true); $this -> aReplace ($this -> aReplaceGrp10); $this -> aReplace ($this -> aReplaceGrp11); $this -> sString = preg_replace( '`(.)\1`', '$1', $this -> sString ); $this -> trimLast (); $this -> getNum (); } /**
  • private function aReplace ()
  • method used to replace letters, given an array
  • @Param array aTab : the replacement array to be used
  • @Param bool bPreg : is the array an array of regular expressions patterns : true => yes`| false => no
  • /
private function aReplace (array $aTab, $bPreg = false) { if (false === $bPreg) { $this -> sString = str_replace (array_keys ($aTab), array_values ($aTab), $this -> sString); } else { $this -> sString = preg_replace (array_keys ($aTab), array_values ($aTab), $this -> sString); } } /**
  • private function trimLast ()
  • method to trim the bad endings
  • /
private function trimLast () { $length = strlen ($this -> sString) - 1; if (in_array ($this -> sString{$length}, $this -> aEnd)) { $this -> sString = substr ($this -> sString, 0, $length); } } /**
  • private static function mapNum ()
  • callback method to create the phonex numeric code, base 22
  • @Param int val : current value
  • @Param int clef : current key
  • @Returns int num : the calculated base 22 value
  • /
private static function mapNum ($val, $clef) { $num = array_search ($val, self::$aReplaceGrp12); $num *= pow (22, - ($clef + 1)); return $num; } /**
  • private function getNum ()
  • method to get a numeric array from the main string
  • we call the callback function mapNum and we add every values of the obtained array to get the final phonex code
  • /
private function getNum () { $aString = str_split ($this -> sString); $aNum = array_map (array ('self', 'mapNum'), array_values ($aString), array_keys ($aString)); $this -> sString = (string) array_sum ($aNum); } } ?>

Conclusion :


Reste des imperfections et des étrangetés...si quelqu'un veut s'amuser à débugger, il le peut. Je fais de même de mon côté :-)

Je remercie Malik qui m'a aidé a débugger un peu l'algo :-) Quelques bugs subsistaient sur l'algo que j'ai suivi.
Maintenant, il ne devrait plus y en avoir!
Seul bémol : php n'utilise pas des nombres de taille suffisemment grande pour que le code phonex base22 soit aussi long que celui de l'algo original.
Je travaille à contourner ce problème, mais j'ai quelques petits soucis avec bcmath :-)
Ceci dit, ça tourne en l'état, et plutôt pas mal même :-)

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

J_G
Messages postés
1406
Date d'inscription
mercredi 17 août 2005
Statut
Membre
Dernière intervention
28 août 2007
6 -
Salut malalam...

Je me souvient avoir aussi essayé de traduire l'algo phone présenté sur developpez.net en PHP.
Bon je n'avais pas fait un code aussi joli que celui présenté ici. Loin de là même !!!

Mais PHONEX a - à mon gout - un très gros défaut : il pondère les sons selon leur position dans le mot. C'est à dire que la premier sylabe compte beaucoup plus que la deuxième. Et ainsi de suite...

Donc :
"grossissement" est plus proche de "grosse" que "gosse" de "grosse" !?!


Donc, j'ai laissé tombé...


Mais par contre, ton code est très joli !!! Bon, j'arrive pas à noté... Je mettrais le 10 plus tard

A+
malalam
Messages postés
10844
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
Hello J_G,

à l'usage, je me suis rendu compte qu'effectivement, phonex était loin de mes attentes.
Au final, je lui préfère soundex2 (que j'ai traduit aussi déviemment, lol, juste avant ce code-ci).
Phonex a des réactions très bizarres de toutes façons. Un peu trop à mon goût. Certes, par momentk il est d'une efficacité impressionnante...mais il est trop incertain.
Soundex2, lui au moins, comporte moins de surprises, même s'il sort plus de mots.
Je l'ai testé (et implémenté d'ailleurs) sur mon code de recherche des villes et codes postaux français...bah, c'est vraiment curieux. Phonex, si on tape 'pari', ne trouve pas 'paris'...lol.

En dehors de ça, je me pose des questions sur la nécessité de coder en base 22...je me demande si on ne peut pas en rester à l'avant dernière étape de l'algo, à savoir conserver un code similaire à ceux de soundex et soundex2 : un code alphanumérique (soundex et soundex2 sont uniquement alpha...). Je pense que c'était simplement pour limiter les ressources lors d'ujn stockage en bdd. Mais je n'en suis pas encore très sûr. Bref je verrai, parce que ce codage en base22 me pose un problème : php n'a pas la puissance de calcul d'un langage 'non web', et il sort nettement moins de chiffres (4 décimales manquent après la virgule par rapport à un calcul effectué en Delphi, par exemple).

Bref...merci, en tous cas, pour le commentaire et la note :-)
J_G
Messages postés
1406
Date d'inscription
mercredi 17 août 2005
Statut
Membre
Dernière intervention
28 août 2007
6 -
Yo !

Je n'ai pas utilisé soundex2... Je vais allé faire un petit tour sur ton code.

Le codage en base 22 permet de "comparer" les mots avec des opérateurs numériques.
Là est la grosse différence avec soudex.

Soundex : ces deux mots ont la même prononciation
phonex : ces deux mots ont une prononciation identique "au millième prés"

Ainsi, tu peux rechercher les mots "pas trop loin". Mais du coup, il y'a pondération :(


PHP peux utiliser des flottant très précis... La configuration par défaut est à 14 chiffres de précision mais y'a une case qui trainent dans le php.ini (? ou le httpd.conf ?) qui permet de changer cette valeur.


Voilou...
malalam
Messages postés
10844
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
Pour la pertinence, j'ai utilise levenshtein pour ma part :
J'ai une liste de noms, et un nom à comparer à cette liste.
Je balance phonex (ou soundex2) pour générer les codes correspondants; je récupère donc une nouvelle liste avec les noms proches de celui recherché. Puis j'applique levenshtein aux noms par rapport au nom recherché (pas leur code hein) : et je classe ensuite ma liste de la distance levenshtein la plus petite à la distance levenshtein la plus grande.
Ca marche pluttôt très bien :-)

Tu veux dire, donc, que 21 mots pas loin d'être identiques, mais pas tout à fait, selon phonex (donc avec 2 codes phonex différents) auront 2 codes phonex proches ? + ou - grand, quoi ?

Je vais jeter un oeil à php.ini alors, moi je n'ai pas 14 chiffres de précision... :-( J'ai essayé avec bcmath, mais sans succès...(je n'ai pas trop cherché non plus en fait).

Mertci en tous cas, vais jeter un oeil :-)
malalam
Messages postés
10844
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
Ah ben en effet, le mien était à 12.
J'ai mis 16 pour avoir la même précision que l'exemple de l'algo.
Ca ne change en tous cas rien aux résultats finaux obtenus (pas plus, pas moins de mots trouvés). Ce dont je me doutais un peu lol.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.