<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://blog.mymind.fr/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Mind... - Tag - PHP</title>
  <link>http://blog.mymind.fr/</link>
  <atom:link href="http://blog.mymind.fr/feed/tag/PHP/rss2" rel="self" type="application/rss+xml"/>
  <description></description>
  <language>fr</language>
  <pubDate>Thu, 01 Dec 2011 21:43:54 +0100</pubDate>
  <copyright>© 2007-2008 Florent Bruneau</copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Afficher des discussions</title>
    <link>http://blog.mymind.fr/post/2007/11/01/Afficher-des-discussions</link>
    <guid isPermaLink="false">urn:md5:1bc6a0f2c9ce16187b5ff9f62df8c2a4</guid>
    <pubDate>Thu, 01 Nov 2007 17:24:00 +0100</pubDate>
    <dc:creator>Florent Bruneau</dc:creator>
        <category>Polytechnique.org</category>
        <category>Banana</category><category>Devel</category><category>PHP</category>    
    <description>&lt;p&gt;Lorsqu'un logiciel a pour vocation d'afficher des discussions, on attend de sa part qu'il nous permette de voir simplement qui répond à qui, dans quel contexte... Ce n'est pas toujours ce qui est le mieux fait. Par exemple, les programmes de fora en ligne à la mode (phpBB par exemple) affiche les discussion comme une succession de rectangles juxtaposés et seul le contenu du message permet de voir qu'il en cite un autre. D'autres logiciels comme Mail.app ont ce défaut et parfois la fâcheuse manie de ne pas vouloir corriger ce problème.&lt;/p&gt;


&lt;p&gt;L'affichage de l'arborescence dans &lt;a href=&quot;http://opensource.polytechnique.org/banana&quot;&gt;Banana&lt;/a&gt; est une des fonctionnalités clés... et elle va beaucoup changer dans la prochaine version.&lt;/p&gt;    &lt;h2&gt;Une ligne par entrée&lt;/h2&gt;


&lt;p&gt;La solution habituelle pour afficher l'arborescence est d'utiliser un message par ligne de telle, des + ou - pour ouvrir ou fermer les noeuds. C'est la solution actuelle de banana.&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.mymind.fr/public/screenshots/old-thread.png&quot; alt=&quot;Banana Thread up to 1.7&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Avec cette solution, on perd très rapidement en lisibilité&amp;nbsp;: dès que la discussion dépasse une vingtaine de messages, l'arborescence devient très haute et plus ça va, plus le titre dérive vers la droite rendant parfois le lien inaccessible. Lorsqu'il y a un troll, il est de fait très courant que certains nouveaux messages se trouvent perdus plusieurs pages en arrière dans l'arborescence, ou que sur certains navigateurs, il soit difficile d'y accéder. De plus l'interface se trouve souvent surchargée, à la limite de la lisibilité&amp;nbsp;: c'est dur de faire tenir un maximum d'informations en un minimum de place en gardant la lisibilité de l'ensemble.&lt;/p&gt;



&lt;h2&gt;Une solution plus visuelle&lt;/h2&gt;


&lt;p&gt;Je ne sais pas combien de personnes connaissent &lt;a href=&quot;http://home.snafu.de/stk/macsoup/&quot;&gt;MacSoup&lt;/a&gt;. Il s'agit d'un petit client NNTP pour MacOS, qui en soit n'a pas beaucoup d'intérêt (il est payant et est relativement limité). Le principal atout de MacSoup est son interface de visualisation des threads (les utilisateurs diront qu'il y a bien plus que l'interface graphique, mais également l'interface clavier etc...). On trouve sur internet quelques captures d'écran en cherchant dans les &lt;a href=&quot;http://www.exalead.com/image/results?q=macsoup%20screenshot&quot;&gt;moteurs de recherche d'image&lt;/a&gt;&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://www.fen-net.de/~xx511/bilder/macsoup/Thread.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Cette interface est compacte, visuelle et permet d'accéder rapidement à n'importe quel message du thread. Pour la prochaine version de Banana, je me suis fortement inspiré de cette interface pour réécrire de 0 l'affichage de l'arborescence. Ceci donne&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.mymind.fr/public/screenshots/new-thread.png&quot; alt=&quot;Thread view in Banana&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Il s'agit de la même discussion que précédemment. On voit donc ici facilement l'arborescence. Lorsqu'un message est non lu, la branche à laquelle il est attaché est noire au lieu de grise ce qui permet de l'identifier du premier coup d'oeil. Les couleurs de fond des noeuds (une idée de &lt;a href=&quot;http://www.falco.bz&quot;&gt;Falco&lt;/a&gt;) sont obtenue à partir d'un hash quelconque sur l'émetteur et permettent donc d'identifier les messages envoyés par la même personne. Lorsqu'on laisse la souris sur un noeud, le nom de l'expéditeur et l'heure du post s'affichent (malheureusement le pointeur de la souris n'apparaît pas sur la capture d'écran)... et bien sûr quand on clique sur un noeud, on va sur le message correspondant.&lt;/p&gt;


&lt;p&gt;Lorsqu'on est sur un message, on garde également la vue du thread ce qui permet de toujours savoir où on est dans la discussion&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.mymind.fr/public/screenshots/new-thread-nav.png&quot; alt=&quot;Thread view with selected message&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Il y a encore un peu de travail à faire pour améliorer les performances de la génération des arbres et pour augmenter sa compacité (éviter les branches qui descendent très bas alors qu'elles auraient pu trouver leur place dans le l'espace vide disponible).&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.mymind.fr/post/2007/11/01/Afficher-des-discussions#comment-form</comments>
      <wfw:comment>http://blog.mymind.fr/post/2007/11/01/Afficher-des-discussions#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.mymind.fr/feed/atom/comments/37</wfw:commentRss>
      </item>
    
  <item>
    <title>La Banane et l'escargot</title>
    <link>http://blog.mymind.fr/post/2007/05/07/La-Banane-et-lescargot</link>
    <guid isPermaLink="false">urn:md5:f4eb2372ed96701c79b70613de7c59fe</guid>
    <pubDate>Mon, 07 May 2007 17:44:00 +0200</pubDate>
    <dc:creator>Florent Bruneau</dc:creator>
        <category>Polytechnique.org</category>
        <category>Banana</category><category>Devel</category><category>PHP</category>    
    <description>&lt;p&gt;La release de &lt;a href=&quot;http://opensource.polytechnique.org/banana/&quot;&gt;Banana&lt;/a&gt; 1.6 en même temps que celle de plat/al 0.9.14 a mis en évidence un certain nombre de faiblesses dans Banana. En particuliers la génération du spool (mise en cache de l'arborescence des messages) et des flux RSS s'est révélé extrêmement lourde pour plusieurs raisons&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l'accès aux mbox des Mailing-Lists nécessite d'appel du &lt;code&gt;mbox-helper&lt;/code&gt;, et donc un &lt;code&gt;fork&lt;/code&gt;... opération lourde, qui répétée plusieurs fois par mbox devient rapidement très lourde lorsqu'on a plusieurs dizaines de Mailing-Lists.&lt;/li&gt;
&lt;li&gt;le traitement des données par PHP est loin d'être immédiats... et il y a clairement des goulots d'étranglement dans le code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C'est pour ces raisons que j'ai passé Banana au &lt;code&gt;profiler&lt;/code&gt;, c'est à dire que j'ai analysé l'exécution de Banana à l'aide d'un outil qui permet de tracer l'exécution du programme et mettant un accent particuliers sur le temps d'exécution de chaque fonction. L'outil que j'ai trouvé pour faire ça est &lt;a href=&quot;http://www.xdebug.org&quot;&gt;xdebug&lt;/a&gt;, utilisé conjointement à &lt;a href=&quot;http://kcachegrind.sourceforge.net/&quot;&gt;KCacheGrind&lt;/a&gt;.&lt;/p&gt;    &lt;h2&gt;Forks&lt;/h2&gt;


&lt;p&gt;Je dois avouer que lorsque j'avais fait mes tests, je n'avais pas plusieurs centaines de Mailing-Lists à traiter... et je ne m'attendais pas à ce qu'une fois passée en production le script de mise à jour des flux RSS puisse prendre toutes les ressources de la machine pendant plusieurs minutes. Il a donc fallu sérieusement restructurer la gestion des accès aux Mailing-Lists pour restreindre au maximum le nombre d'accès au &lt;code&gt;mbox-helper&lt;/code&gt; (un petit programme écrit en C qui se charge de tous les accès aux mbox), et, en cas d'accès, limiter au maximum le temps passer sur le &lt;code&gt;mbox-helper&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Donc désormais le &lt;code&gt;mbox-helper&lt;/code&gt; n'est plus appelé que si la mbox a changé depuis le dernier passage (le changement étant détecté par la taille du fichier). Ce qui permet donc de supprimer le lancement d'environ 500 &lt;code&gt;mbox-helper&lt;/code&gt; lors des rafraîchissements des spools (en effet, un nombre négligeable de Mailing-List aura des nouveaux messages lors du passage du script toutes les 5, 10 ou 20 minutes). Ajouté à cela la correction d'un bug qui faisait que l'appel au &lt;code&gt;mbox-helper&lt;/code&gt; oubliait de spécifier l'offset où chercher le message à traiter et qui forçait donc le &lt;code&gt;mbox-helper&lt;/code&gt; à relire la totalité de la mbox, on peut se permettre de supposer que la prochaine version de Banana sera plus efficace pour la gestion des mbox.&lt;/p&gt;



&lt;h2&gt;Array_shift&lt;/h2&gt;


&lt;h3&gt;Piles&lt;/h3&gt;


&lt;p&gt;Il est souvent extrêment pratique d'utiliser une pile de données. Cela permet de traiter les informations dans l'ordre de la pile sans excès de mémoire puisque chaque élément est dépilé avant d'être traité. C'est une technique que j'aime particulièrement lorsque j'ai une suite de lignes à traiter&amp;nbsp;: je prend un tableau contenant une ligne par entrée et je le parcours avec &lt;code&gt;array_shift&lt;/code&gt; qui permet de dépiler le premier élément du tableau. On obtient ainsi un code de la forme&lt;/p&gt;

&lt;pre&gt;
[php]
while (!is_null($line = array_shift($lines))) {
    do_something($line);
}
do_something($lines);
&lt;/pre&gt;


&lt;p&gt;Un &lt;code&gt;foreach&lt;/code&gt; peut très bien faire la même chose, mais on perd les avantages de la piles. Avec cette structure, pour avoir le même comportement que la boucle précédente, il faut ajouter un &lt;code&gt;unset()&lt;/code&gt;&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
[php]
foreach ($lines as $key=&amp;gt;&amp;amp;$value) {
    do_something($line);
    unset($lines[$key]);
}
do_something($lines);
&lt;/pre&gt;


&lt;p&gt;Certes le &lt;code&gt;foreach&lt;/code&gt; sera sensiblement plus rapide que le &lt;code&gt;while&lt;/code&gt;/&lt;code&gt;array_shift()&lt;/code&gt; car il comprend un appel de fonction à chaque itération, mais on peut s'attendre raisonnablement à ce que cet appel soit en O(1), et ait donc un coût négligeable.&lt;/p&gt;


&lt;h3&gt;Profiler&lt;/h3&gt;


&lt;p&gt;Là où il y a un problème c'est que le profiler m'indique que &lt;code&gt;array_shift&lt;/code&gt; prend 57% du temps d'exécution de Banana. Ces 57% sont partagés entre 80000 appels à la fonction, mais le plus marquant c'est que parmi ces 80000 appels, ce ne sont que 24000 d'entre eux qui prennent la quasi-totalité du temps. Pourquoi ces appels particuliers sont-ils si lourd alors que les 60000 autres ont un coût parfaitement négligeable.&lt;/p&gt;


&lt;p&gt;La seule différence entre ces deux cas d'appels c'est que les &lt;em&gt;lourds&lt;/em&gt; traitent un énorme tableau de 24000 lignes, alors que les &lt;em&gt;légers&lt;/em&gt; traitent un grand nombre de petits tableaux de quelques dizaines de lignes chacun. Il est donc extrêmement clair que &lt;code&gt;array_shift&lt;/code&gt; &lt;strong&gt;n'est pas&lt;/strong&gt; une fonction en O(1)... J'ai donc changé la structure de code qui reflétait la structure de données par une simple boucle qui perd en lisibilité dans &lt;a href=&quot;http://opensource.polytechnique.org/viewsvn/diff.php?path=/trunk/banana/mbox.inc.php&amp;amp;rev=248&amp;amp;repname=Banana&quot;&gt;ce patch&lt;/a&gt;. Après ce changement, les 60000 &lt;code&gt;array_shift&lt;/code&gt; restant ne prennent que 0.16% du temps d'exécution de Banana...&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.mymind.fr/post/2007/05/07/La-Banane-et-lescargot#comment-form</comments>
      <wfw:comment>http://blog.mymind.fr/post/2007/05/07/La-Banane-et-lescargot#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.mymind.fr/feed/atom/comments/25</wfw:commentRss>
      </item>
    
  <item>
    <title>Deux approches...</title>
    <link>http://blog.mymind.fr/post/2007/03/28/Deux-approches</link>
    <guid isPermaLink="false">urn:md5:659d3158b09c841e3e595d336e25a842</guid>
    <pubDate>Wed, 28 Mar 2007 00:52:00 +0200</pubDate>
    <dc:creator>Florent Bruneau</dc:creator>
        <category>GeekTime</category>
        <category>Devel</category><category>PHP</category><category>Qt</category>    
    <description>&lt;p&gt;Les deux APIs avec lesquelles j'ai eu le plus la chance de travailler sont celle de &lt;a href=&quot;http://doc.trolltech.com/&quot;&gt;Qt&lt;/a&gt; et celle de &lt;a href=&quot;http://www.php.net&quot;&gt;PHP&lt;/a&gt;... il n'y a pas à dire, entre ces deux bibliothèques, c'est comme le jour et la nuit.&lt;/p&gt;    &lt;p&gt;L'API de Qt est intuitive et est conçue pour l'être. Si je veux avoir l'âge du capitaine, je ferais toujours &lt;code&gt;capitaine-&amp;gt;age()&lt;/code&gt;, et si je veux définir son âge, ce sera &lt;code&gt;capitaine-&amp;gt;setAge(42)&lt;/code&gt;. Cette API est tellement prévisible qu'au bout de quelques jours d'utilisation, on n'utilise la documentation que pour chercher de nouvelles fonctionnalités, mais très rarement pour chercher des fonctions courantes. En plus, ce qui est remarquable, c'est qu'au fur et à mesure des versions de Qt, l'accent est mis sur la simplification de l'API... Qt 4 est bien plus intuitive que Qt 3 qui est pourtant déjà très facile à utiliser.&lt;/p&gt;


&lt;p&gt;La qualité de la documentation de Qt y est également pour quelque chose&amp;nbsp;: toutes les méthodes sont documentées, avec la plupart du temps un exemple. Le tout étant très facilement accessible grâce à l'assistant, un petit programme qui permet de consulter la documentation en intégrant des outils pour naviguer simplement entre les pages.&lt;/p&gt;


&lt;p&gt;Par contre, je ne sais pas pourquoi, mais en PHP, l'API est incohérente... l'exemple le plus simple est sans doute celui du remplacement et de la recherche dans une variable. Si, par exemple, je veux rechercher un élément&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
[php]
//Dans une chaîne de caractères :
strpos($string, $findme);
//Dans un tableau, c'est l'inverse :
in_array($findme, $array);
array_search($findme, $array);
&lt;/pre&gt;


&lt;p&gt;Si je veux faire un remplacement dans une chaîne de caractères&amp;nbsp;:&lt;/p&gt;
&lt;pre&gt;
[php]
strtr($string, $search, $replace);
str_replace($search, $replace, $string);
preg_replace($search, $replace, $string);
&lt;/pre&gt;


&lt;p&gt;Chic&amp;nbsp;! Grâce à ça, même si je connais le nom de la fonction à utiliser, je suis quasiment forcé à chaque fois d'aller regarder dans la documentation de PHP (ce que j'ai fait pour écrire ce paragraphe, sinon j'aurais &lt;em&gt;'forcément&lt;/em&gt;' inversé les arguments)... heureusement que celle-ci est bien faite et permet de trouver rapidement les fonctions dont on connaît le nom.&lt;/p&gt;


&lt;p&gt;Quand est-ce que PHP se décidera à revoir son API pour qu'elle soit aussi intuitive que celle de Qt&amp;nbsp;? Malheureusement, sans doute jamais&amp;nbsp;: il faut garder (autant que possible) la compatibilité avec les versions précédentes de PHP. Dommage, parce qu'à mon avis, si PHP continue à étoffer son API sans la rendre cohérente, sa popularité va diminuer progressivement dans les années qui viennent.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.mymind.fr/post/2007/03/28/Deux-approches#comment-form</comments>
      <wfw:comment>http://blog.mymind.fr/post/2007/03/28/Deux-approches#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.mymind.fr/feed/atom/comments/17</wfw:commentRss>
      </item>
    
  <item>
    <title>Soundex Français</title>
    <link>http://blog.mymind.fr/post/2007/03/15/Soundex-Francais</link>
    <guid isPermaLink="false">urn:md5:0b44d76db88f4470bb76e4bc56c891fe</guid>
    <pubDate>Thu, 15 Mar 2007 18:06:00 +0100</pubDate>
    <dc:creator>Florent Bruneau</dc:creator>
        <category>Polytechnique.org</category>
        <category>Devel</category><category>PHP</category>    
    <description>&lt;p&gt;Pour faire une recherche phonétique, on utilise souvent ce qu'on appelle une transcription &lt;strong&gt;soundex&lt;/strong&gt; des mots. C'est une réécriture du mot, dans un alphabet restreint et sur un nombre de caractères restreint également. La plupart des algorithmes qu'on peut trouver sur internet sont conçus uniquement à la langue anglaise. Pour utiliser la recherche phonétique en français, il faut donc adapter cet algorithme.&lt;/p&gt;


&lt;p&gt;L'implémentation française la plus courante utilise l'&lt;a href=&quot;http://sqlpro.developpez.com/cours/soundex/&quot;&gt;algorithme décrit par Frédéric Brouard&lt;/a&gt;. Malheureusement cet algorithme ne me satisfait pas vraiment, car il n'est finalement pas très adapté à langue française.&lt;/p&gt;    &lt;p&gt;En effet cet algorithme a pas mal de défauts&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;il est très aléatoire dans le cas de dédoublement de consonnes. Par exemple, il ne permet de trouver que &lt;em&gt;bananne&lt;/em&gt; (soundex &lt;code&gt;BNN&lt;/code&gt;) est une approximation de &lt;em&gt;banane&lt;/em&gt; (soundex &lt;code&gt;BN&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;il est très peu sélectif sur les voyelles. Par exemple &lt;em&gt;poulpe&lt;/em&gt; et &lt;em&gt;palpa&lt;/em&gt; ont le même soundex (&lt;code&gt;PLP&lt;/code&gt;) alors que pour moi, poulpe et palpa sont deux mots éloignés, malgré leur consonnes communes. Par contre, il ne reconnaîtra pas Aymeric (soundex &lt;code&gt;AYMR&lt;/code&gt;) comme étant un homophone de Emeric (soundex &lt;code&gt;EMRC&lt;/code&gt;)...&lt;/li&gt;
&lt;li&gt;il n'est pas vraiment capable de distinguer les conjugaisons. Par exemple &lt;em&gt;palper&lt;/em&gt; (soundex &lt;code&gt;PLPR&lt;/code&gt;) et &lt;em&gt;palpé&lt;/em&gt; (soundex &lt;code&gt;PLP&lt;/code&gt;) sont éloignés l'un de l'autre.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;J'ai donc réalisé une nouvelle implémentation de &lt;code&gt;soundex&lt;/code&gt; qui est plus adaptée (à mon sens) à la langue française. Je n'ai évidemment pas pour ambition de réaliser une solution parfaite (ce qui est impossible à faire à partir d'un système automatisé sans dictionnaire).&lt;/p&gt;


&lt;h2&gt;Algorithme&lt;/h2&gt;


&lt;p&gt;Mon algorithme, comme je l'ai déjà dit, est une adaptation de celui de &lt;a href=&quot;http://sqlpro.developpez.com/cours/soundex/&quot;&gt;Frédéric Brouard&lt;/a&gt;. Il se compose de 2 étapes principales&amp;nbsp;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;le préformatage, qui consiste à transformer le chaîne de caractère brute, en une chaîne analysable.&lt;/li&gt;
&lt;li&gt;l'analyse de la chaîne, et la recherche de entités phoniques élémentaires qui la composent&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Préformatage&lt;/h3&gt;


&lt;p&gt;Le préformatage est extrêmement simple&amp;nbsp;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On converti la chaîne en majuscule&lt;/li&gt;
&lt;li&gt;On converti chaque caractère accentué vers son caractère non-accentué correspondant&lt;/li&gt;
&lt;li&gt;On filtre pour ne conserver que les lettres (de A à Z)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ainsi après ce premier filtrage, &lt;em&gt;Mac-Cartney&lt;/em&gt; devient &lt;em&gt;MACCARTNEY&lt;/em&gt; et &lt;em&gt;palpé&lt;/em&gt; sera &lt;em&gt;PALPE&lt;/em&gt;&lt;/p&gt;


&lt;h3&gt;Traitement des données&lt;/h3&gt;


&lt;p&gt;Le traitement des données consiste à reconnaître les sonorités. Pour ceci on utilise la table de conversion suivante qui associe à chaque sonorité complexe un caractère&amp;nbsp;:&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;&lt;th&gt;Combinaison&lt;/th&gt;&lt;th&gt;Caractère&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;G (prononcé GUE), C (prononcé Q), CK, K, QU, Q&lt;/td&gt;&lt;td&gt;K&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;CH, SH, SCH&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;ST&lt;/td&gt;&lt;td&gt;T&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;PF (en début de mot), PH&lt;/td&gt;&lt;td&gt;F&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Z (en fin de mot)&lt;/td&gt;&lt;td&gt;ZE&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Z, ZZ, C (prononcé SE)&lt;/td&gt;&lt;td&gt;S&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;G (prononcé JE)&lt;/td&gt;&lt;td&gt;J&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;EAU, AU&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;IN, UN, AIN, EIN (proncés UN)&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;AON, AOM, EN, AN (prononcés EN)&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;EY, EI, AY, AI, OE, OEU, EU, ER&lt;/td&gt;&lt;td&gt;E&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;OI&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;ILLE, I&lt;/td&gt;&lt;td&gt;Y&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;OU, OW&lt;/td&gt;&lt;td&gt;U&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;ON, OM&lt;/td&gt;&lt;td&gt;O&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;KN (en début de mot)&lt;/td&gt;&lt;td&gt;N&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;



&lt;p&gt;Une fois qu'on a cette table de correspondances (elle n'est peut-être pas exhaustive... je n'hésiterais pas à l'améliorer), on applique toutes les modifications, on supprime les lettres qui sont présentes en doublon et finalement on supprime les H restants. Il faut également supprimer les caractères muets en fin de mot. En français, ce seront les X, T, D, S (et le L qui les précède si il existe) ou les E.&lt;/p&gt;


&lt;p&gt;Ensuite, il faut faire un nettoyage sur la chaîne de caractère. Ce nettoyage consiste à rechercher les phonèmes importants et à supprimer les caractères muets ou peu audibles&amp;nbsp;: on veut identifier ce qui fait la particularité d'une syllabe. Frédéric Brouard considère que toutes les voyelles (exceptés les Y précédés d'une voyelle) placées autre part qu'en début de mot sont insignifiantes... ça me paraît le gros point faible de sa méthode. Personnellement j'ai essayé d'identifier une liste de sonorité dominantes. Voici celles que j'ai actuellement&amp;nbsp;:&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;&lt;th&gt;Sonorité&lt;/th&gt;&lt;th&gt;Informations&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;K, T, P&lt;/td&gt;
    &lt;td&gt;Ce sont les consonnes qui sont très marquées, qui rythment le mot&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;A, U, O, 1&lt;/td&gt;
    &lt;td&gt;Ce sont les son qu'on entend le mieux, contrairement aux I ou aux E qui mettent plus en avant les consonnes. Le A est à mon avis particulier, car il est faible mais suffisamment marquant pour effacer des consonnes comme le R&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Y&lt;/td&gt;
    &lt;td&gt;Y n'est marquant que lorsqu'il a un rôle de consonne : quand il permet de lier deux parties du mot, comme dans ''voyelle'', où il lie le ''vo'' et le ''elle''. Il est donc important lorsqu'il est encadré par une ou des voyelles. Dans les autres cas, c'est une voyelle faible&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;L&lt;/td&gt;
    &lt;td&gt;Contrairement aux K, T et P, le L permet d'adoucir le mot, en particulier lorsqu'il sert de liaison entre une consonne et une voyelle. Il est donc important de le conserver&lt;/tr&gt;
  &lt;/tr&gt;
&lt;/table&gt;



&lt;p&gt;Ces choix sont relativement arbitraires. Le comportement de l'algorithme est alors simple&amp;nbsp;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Si une voyelle est liée à une consonne de liaison (qui fait le lien entre la voyelle et une autre consonne, c'est souvent le cas du L ou du R, on exclut de ce cas les consommes fortes)&lt;/li&gt;
&lt;li&gt;Si en plus il s'agit d'une voyelle forte
&lt;ul&gt;
&lt;li&gt;alors, on supprime la consonne de liaison et on garde la voyelle&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensuite, on supprime les voyelles faibles (sauf si elles commencent le mot). Pour cette suppression, on considère le A comme une voyelle faible.&lt;/p&gt;


&lt;p&gt;Pour terminer on coupe la chaîne obtenue à 4 caractères (mais il est parfaitement envisageable de prendre un soundex sur plus (ou moins) de caractères. Simplement, dans la plupart des cas, 4 caractères suffisent amplement à avoir un mot significatif.&lt;/p&gt;


&lt;h2&gt;Implémentation&lt;/h2&gt;


&lt;p&gt;Mon implémentation en PHP est téléchargeable &lt;a href=&quot;http://blog.mymind.fr/soundex_fr.php?action=download&quot;&gt;ici&lt;/a&gt;. Elle est testable &lt;a href=&quot;http://blog.mymind.fr/mind/post/2007/03/15/Soundex-Francais#test&quot;&gt;plus bas dans cette même page&lt;/a&gt;. Voici le code de la fonction soundex_fr (je ne garantis pas que le code disponible sur cette page reste constamment à jour, contrairement au lien de téléchargement) :&lt;sup&gt;[&lt;a href=&quot;http://blog.mymind.fr/post/2007/03/15/Soundex-Francais#pnote-14-1&quot; id=&quot;rev-pnote-14-1&quot;&gt;1&lt;/a&gt;]&lt;/sup&gt;&lt;/p&gt;

&lt;pre&gt;
[php]
function soundex_fr($sIn) 
{ 
    static $convVIn, $convVOut, $convGuIn, $convGuOut, $accents; 
    if (!isset($convGuIn)) { 
        $accents = array('É' =&amp;gt; 'E', 'È' =&amp;gt; 'E', 'Ë' =&amp;gt; 'E', 'Ê' =&amp;gt; 'E', 
                    'Á' =&amp;gt; 'A', 'À' =&amp;gt; 'A', 'Ä' =&amp;gt; 'A', 'Â' =&amp;gt; 'A', 'Å' =&amp;gt; 'A', 'Ã' =&amp;gt; 'A', 
                    'Ï' =&amp;gt; 'I', 'Î' =&amp;gt; 'I', 'Ì' =&amp;gt; 'I', 'Í' =&amp;gt; 'I', 
                    'Ô' =&amp;gt; 'O', 'Ö' =&amp;gt; 'O', 'Ò' =&amp;gt; 'O', 'Ó' =&amp;gt; 'O', 'Õ' =&amp;gt; 'O', 'Ø' =&amp;gt; 'O', 
                    'Ú' =&amp;gt; 'U', 'Ù' =&amp;gt; 'U', 'Û' =&amp;gt; 'U', 'Ü' =&amp;gt; 'U', 
                    'Ç' =&amp;gt; 'C', 'Ñ' =&amp;gt; 'N', 'Ç' =&amp;gt; 'S', '¿' =&amp;gt; 'E', 
                    'é' =&amp;gt; 'e', 'è' =&amp;gt; 'e', 'ë' =&amp;gt; 'e', 'ê' =&amp;gt; 'e', 
                    'á' =&amp;gt; 'a', 'à' =&amp;gt; 'a', 'ä' =&amp;gt; 'a', 'â' =&amp;gt; 'a', 'å' =&amp;gt; 'a', 'ã' =&amp;gt; 'a', 
                    'ï' =&amp;gt; 'i', 'î' =&amp;gt; 'i', 'ì' =&amp;gt; 'i', 'í' =&amp;gt; 'i', 
                    'ô' =&amp;gt; 'o', 'ö' =&amp;gt; 'o', 'ò' =&amp;gt; 'o', 'ó' =&amp;gt; 'o', 'õ' =&amp;gt; 'o', 'ø' =&amp;gt; 'o', 
                    'ú' =&amp;gt; 'u', 'ù' =&amp;gt; 'u', 'û' =&amp;gt; 'u', 'ü' =&amp;gt; 'u', 
                    'ç' =&amp;gt; 'c', 'ñ' =&amp;gt; 'n'); 
        $convGuIn  = array( 'GUI', 'GUE', 'GA', 'GO', 'GU', 'SCI', 'SCE', 'SC', 'CA', 'CO', 
                            'CU', 'QU', 'Q', 'CC', 'CK', 'G', 'ST', 'PH'); 
        $convGuOut = array( 'KI', 'KE', 'KA', 'KO', 'K', 'SI', 'SE', 'SK', 'KA', 'KO', 
                            'KU', 'K', 'K', 'K', 'K', 'J', 'T', 'F'); 
        $convVIn   = array( '/E?(AU)/', '/([EA])?[UI]([NM])([^EAIOUY]|$)/', '/[AE]O?[NM]([^AEIOUY]|$)/', 
                            '/[EA][IY]([NM]?[^NM]|$)/', '/(^|[^OEUIA])(OEU|OE|EU)([^OEUIA]|$)/', '/OI/', 
                            '/(ILLE?|I)/', '/O(U|W)/', '/O[NM]($|[^EAOUIY])/', '/(SC|S|C)H/', 
                            '/([^AEIOUY1])[^AEIOUYLKTPNR]([UAO])([^AEIOUY])/', '/([^AEIOUY]|^)([AUO])[^AEIOUYLKTP]([^AEIOUY1])/', '/^KN/', 
                            '/^PF/', '/C([^AEIOUY]|$)/',  '/E(Z|R)$/', 
                            '/C/', '/Z$/', '/(?&amp;lt;!^)Z+/', '/H/', '/W/'); 
        $convVOut  = array( 'O', '1\\3', 'A\\1', 
                            'E\\1', '\\1E\\3', 'O', 
                            'Y', 'U', 'O\\1', '9',- 
                            '\\1\\2\\3', '\\1\\2\\3', 'N', 
                            'F', 'K\\1', 'E', 
                            'S', 'SE', 'S', '', 'V'); 
    } 
    // Si il n'y a pas de mot, on sort immédiatement
    if ( $sIn === '' ) return '    '; 
    // On supprime les accents- 
    $sIn = strtr( $sIn, $accents); 
    // On met tout en minuscule- 
    $sIn = strtoupper( $sIn ); 
    // On supprime tout ce qui n'est pas une lettre
    $sIn = preg_replace( '`[^A-Z]`', '', $sIn ); 
    // Si la chaîne ne fait qu'un seul caractère, on sort avec.
    if ( strlen( $sIn ) === 1 ) return $sIn . '   '; 
    // on remplace les consonnances primaires
    $sIn = str_replace( $convGuIn, $convGuOut, $sIn ); 
    // on supprime les lettres répétitives 
    $sIn = preg_replace( '`(.)\\1`', '$1', $sIn ); 
    // on réinterprète les voyelles 
    $sIn = preg_replace( $convVIn, $convVOut, $sIn); 
 
    // on supprime les terminaisons T, D, S, X (et le L qui précède si existe)
    $sIn = preg_replace( '`L?[TDX]?S?$`', '', $sIn ); 
    // on supprime les E, A et Y qui ne sont pas en première position 
    $sIn = preg_replace( '`(?!^)Y([^AEOU]|$)`', '\\1', $sIn); 
    $sIn = preg_replace( '`(?!^)[EA]`', '', $sIn); 
    return substr( $sIn . '    ', 0, 4); 
} 
&lt;/pre&gt;


&lt;h2&gt;Tests&lt;/h2&gt;


&lt;p&gt;Avec cette implémentation, on a par exemple&amp;nbsp;:&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;&lt;th&gt;Mot&lt;/th&gt;&lt;th&gt;Soundex&lt;/th&gt;&lt;th&gt;Mot&lt;/th&gt;&lt;th&gt;Soundex&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Aymeric&lt;/td&gt;&lt;td&gt;EMRK&lt;/td&gt;&lt;td&gt;Emeric&lt;/td&gt;&lt;td&gt;EMRK&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Banane&lt;/td&gt;&lt;td&gt;BNN&lt;/td&gt;&lt;td&gt;Bananne&lt;/td&gt;&lt;td&gt;BNN&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Palper&lt;/td&gt;&lt;td&gt;PLP&lt;/td&gt;&lt;td&gt;Palpé&lt;/td&gt;&lt;td&gt;PLP&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Palpa&lt;/td&gt;&lt;td&gt;PLP&lt;/td&gt;&lt;td&gt;Poulpe&lt;/td&gt;&lt;td&gt;PULP&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Mario&lt;/td&gt;&lt;td&gt;MRYO&lt;/td&gt;&lt;td&gt;Marion&lt;/td&gt;&lt;td&gt;MRYO&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;



&lt;p&gt;On a par contre des résultats moyens lorsque des H séparent des voyelles faibles&amp;nbsp;: par exemple &lt;em&gt;Mouahaha&lt;/em&gt; donne MU (contre M avec la version précédente).&lt;/p&gt;


&lt;p&gt;&lt;a name=&quot;test&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://blog.mymind.fr/ajax.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
function get_soundex()
{
    Ajax.update_html(&quot;sdx_result&quot;, &quot;/soundex_fr.php?text=&quot; + encodeURIComponent(document.getElementById(&quot;sdx_form&quot;).value));
    return false;
}
&lt;/script&gt;
&lt;div&gt;
&lt;form action=&quot;http://blog.mymind.fr/post/2007/03/15/&quot; method=&quot;get&quot; onsubmit=&quot;return false;&quot;&gt;
   &lt;p&gt;
      &lt;strong&gt;Entrer un texte à convertir : &lt;/strong&gt;
      &lt;input type=&quot;text&quot; id=&quot;sdx_form&quot; name=&quot;sdx_form&quot; value=&quot;&quot; /&gt;
      &lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Obtenir le Soundex&quot;  onclick=&quot;get_soundex(); return false;&quot;/&gt;&lt;br /&gt;
      &lt;strong&gt;Résultat : &lt;span id=&quot;sdx_result&quot; style=&quot;color: red&quot;&gt; &lt;/span&gt;&lt;/strong&gt;
   &lt;/p&gt;
&lt;/form&gt;
&lt;/div&gt;



&lt;p&gt;&lt;em&gt;N'hésitez pas à faire des tests et à me faire part des résultats qui semblent anormaux.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;Conclusion&lt;/h2&gt;


&lt;p&gt;Cette version est loin d'être parfaite, mais elle est plus adaptée au français que celle de Frédéric Brouard. Elle n'est d'ailleurs pas terminée et risque d'évoluer dans un futur proche, en particuliers pour ce qui est des tables de transcriptions et de prédominances.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;&lt;h4&gt;Notes&lt;/h4&gt;
&lt;p&gt;[&lt;a href=&quot;http://blog.mymind.fr/post/2007/03/15/Soundex-Francais#rev-pnote-14-1&quot; id=&quot;pnote-14-1&quot;&gt;1&lt;/a&gt;] L'algorithme prend en compte les modifications fournies par les lecteurs et dont vous retrouverez le détail dans les commentaires. Merci donc aux commentateurs, et particulièrement à &lt;a href=&quot;http://azur.ironie.org/&quot;&gt;David&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;
</description>
    
    
    
          <comments>http://blog.mymind.fr/post/2007/03/15/Soundex-Francais#comment-form</comments>
      <wfw:comment>http://blog.mymind.fr/post/2007/03/15/Soundex-Francais#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.mymind.fr/feed/atom/comments/14</wfw:commentRss>
      </item>
    
  <item>
    <title>Un client RSS pour lire les Forums NNTP et les Mailing-Lists...</title>
    <link>http://blog.mymind.fr/post/2007/02/25/Un-client-RSS-pour-lire-les-Forums-NNTP-et-les-Mailing-Lists</link>
    <guid isPermaLink="false">urn:md5:ccec4d2a8525474fc851b3082b3f91ad</guid>
    <pubDate>Sun, 25 Feb 2007 19:05:00 +0100</pubDate>
    <dc:creator>Florent Bruneau</dc:creator>
        <category>Polytechnique.org</category>
        <category>Banana</category><category>Devel</category><category>Mailing-Lists</category><category>NNTP</category><category>PHP</category><category>RSS</category>    
    <description>    &lt;p&gt;Si il y a un bout de code dont je suis content au sein de &lt;a href=&quot;https://www.polytechnique.org&quot;&gt;Polytechnique.org&lt;/a&gt;, c'est &lt;a href=&quot;http://opensource.polytechnique.org/banana/&quot;&gt;Banana&lt;/a&gt;. Banana est à l'origine un client Web pour le protocole NNTP (c'est à dire le protocole utilisé par Usenet). Depuis sa dernière version (la 1.5), Banana est capable d'utiliser n'importe quel protocole comme source (les protocoles actuellement implémentés étant le NNTP et la lecture de MBox, mais on pourrait envisager d'ajouter le support des Maildir ou d'IMAP sans problème), ainsi, à Polytechnique.org, nous utilisons Banana pour offrir une plateforme Web vers &lt;a href=&quot;https://www.polytechnique.org/banana&quot;&gt;nos forums&lt;/a&gt; et pour mettre en ligne les archives des Mailing-Lists que nous hébergeons.&lt;/p&gt;


&lt;p&gt;Banana est à mon avis un bon outils&amp;nbsp;: il permet un rendu plus que correct de la plupart des mails HTML (même ceux dont le formatage est défini dans une feuille de style), il affiche les discussions proprement et rapidement (grâce à un système de cache du côté serveur). C'est donc un gros plus en comparaison des interfaces habituelles de consultation d'archives de Mailing-List qu'on peut trouver sur Internet... et puis, ce n'est pas un forum php, pas de fioritures à la phpbb&amp;nbsp;: Banana est clair, lisible et rapide... et facile à installer. Pour s'en convaincre, il suffit de regarder la &lt;a href=&quot;http://opensource.polytechnique.org/viewsvn/filedetails.php?repname=Banana&amp;amp;path=/trunk/examples/index.php&amp;amp;rev=0&amp;amp;sc=1&quot;&gt;fichier d'exemple&lt;/a&gt; qui fournit un banana totalement fonctionnel (à chacun ensuite d'y ajouter sa couche d'authentification si nécessaire).&lt;/p&gt;


&lt;p&gt;La grande nouveauté de la prochaine version de Banana est l'intégration de flux RSS... ainsi il sera possible de lire les forums sans se connecter au site et sans client news, ou de suivre les discussions des mailing-lists sans relever son courrier (le premier des 2 cas étant certainement le plus intéressant). Pour les utilisateurs de Polytechnique.org, la fonctionnalité est d'ores et déjà disponible sur &lt;a href=&quot;http://dev.m4x.org/~x2003bruneau/banana&quot;&gt;ma version de développement&lt;/a&gt;. Deux types de flux son disponibles&amp;nbsp;: soit un flux par groupe, soit un flux regroupant tous les groupes auxquels on est abonné.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.mymind.fr/post/2007/02/25/Un-client-RSS-pour-lire-les-Forums-NNTP-et-les-Mailing-Lists#comment-form</comments>
      <wfw:comment>http://blog.mymind.fr/post/2007/02/25/Un-client-RSS-pour-lire-les-Forums-NNTP-et-les-Mailing-Lists#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.mymind.fr/feed/atom/comments/4</wfw:commentRss>
      </item>
    
</channel>
</rss>
