WdBlog

30 déc

Supprimer les accents des caractères accentués Posté dans PHP 10 commentaires

Voilà un grand classique pour nous autres peuples de caractères : comment supprimer les accents des caractères accentués de notre sublime langue en utilisant PHP ? Mais avant de répondre à cette question, une autre se forme, insidieuse : pourquoi supprimer les accents des caractères accentués ? Voici quelques exemples envisageables :

  • Transformer une chaine en URL plus facilement lisible : "severite" au lieu de "s%C3%A9v%C3%A9rit%C3%A9"
  • Transformer une chaine pour obtenir un nom de fichier propre
  • Pouvoir enfin ordonner les clés accentuées de nos tableaux associatifs PHP et ne plus retrouver "événement" après "zéro" !

Solution toute nulle

C'est ce dernier exemple qui m'a conduit à rechercher une solution viable. En effet, malgré tous les efforts de Google je n'ai réussi à trouver que des exemples utilisant des remplacements de caractères du style :

<?php

$str = strtr($str'ÁÀÂÄÃÅÇÉÈÊËÍÏÎÌÑÓÒÔÖÕÚÙÛÜÝ''AAAAAACEEEEEIIIINOOOOOUUUUY');
$str = strtr($str'áàâäãåçéèêëíìîïñóòôöõúùûüýÿ''aaaaaaceeeeiiiinooooouuuuyy');

?>

Évidement, ça a l'air sexy et simple, mais si comme moi vous travaillez en utf-8, les caractères accentués sont bien souvent encodés avec plus de 8bits (un caractère), mais jusqu'à 32bits. Ainsi, les techniques basées sur une substitution de caractères sont totalement obsolètes parce qu'elles produisent des résultats erronés. C'est triste, mais c'est vrai.

Solution pas mal

Comment faire alors ? Sommes nous condamnés par des siècles d'histoire ? Que nenni ! C'est en transformant d'anciens fichiers iso-8859-1 en utf-8 que la solution m'a sauté au visage : utiliser les entités échappées pour récupérer le caractère non accentué. En effet, "é" une fois échappé donne "&eacute" et "É" donne "&Eacute". Ne reste plus alors qu'à gérer les cas particuliers comme la cédille ou les ligatures, et le tour et joué. Ainsi, Nous utilisons simplement htmlentities() pour échapper les caractères exotiques puis remplaçons les expressions échappées grâce à de savantes expressions régulières :

<?php

function wd_remove_accents($str$charset='utf-8')
{
    $str = htmlentities($str, ENT_NOQUOTES, $charset);
    
    $str = preg_replace('#\&([A-za-z])(?:acute|cedil|circ|grave|ring|tilde|uml)\;#''\1'$str);
    $str = preg_replace('#\&([A-za-z]{2})(?:lig)\;#''\1'$str)// pour les ligatures e.g. '&oelig;'
    $str = preg_replace('#\&[^;]+\;#'''$str)// supprime les autres caractères
    
    return $str;
}

?>

Trop fastoche !

Notez tout de même que je préfère supprimer les caractères échappés qui n'ont pas été traités. C'est pas la meilleure solution, mais c'est celle qui me va le mieux…

Et maintenant, que vais-je faire ?

Dans un prochain billet nous verrons comment utiliser cette fonction pour ordonner les clés accentuées d'un tableau associatif. Que d'aventures !! À la prochaine !

Poster un commentaire

 
 

10 Florent V. a écrit : Vendredi 09 Mai 2008 à 22:20

Hello,

« mais si comme moi vous travaillez en utf-8, les caractères accentués sont bien souvent encodés avec plus qu'un seul caractère (jusqu'à trois en fait) »

Non, un caractère est un caractère et pas potentiellement une suite de plusieurs caractères. Par contre, un caractère peut être écrit avec plusieurs octets. Suivant les caractères, cela va de un à quatre (et non pas trois) pour l'UTF-8.

Attention donc à ne pas confondre octets et caractères. ;)

9 gofromiel a écrit : Samedi 10 Mai 2008 à 17:17

Tu fais bien de préciser cela Florent. J'ai préféré – à tord – parler de « caractères » plutôt que de « bits » parce que tout le monde n'a pas fait de l'assembleur (680×0 pour moi) et que le PHP est souvent un premier langage de programmation.

J'ai corrigé ces erreurs, je ne sais pas ce qui m'a prit.
Vulgarisation quand tu nous tiens :-)

8 bibi a écrit : Mardi 20 Mai 2008 à 12:58

Merci beaucoup pour ton petit bout de code. J'avais le même problème que toi et ça m'a bien aidé! Ton site est vraiment très intéressant on y apprend pleins de choses continue! ^^

7 6sko59 a écrit : Jeudi 05 Juin 2008 à 18:06

Salut, Je tiens juste à te remercier. Ta fonction est super , elle m'a bien dépanné!!

6sko59

6 Maxime a écrit : Dimanche 08 Juin 2008 à 01:23

Thanks man,

comme quoi des fois à 2h du mat on trouve du bon sur le net …

5 K a écrit : Lundi 09 Juin 2008 à 14:46

À moins d'avoir à utiliser des encodages extra (qui pour la plupart ne sont pas gérés par htmlentities [cf. la doc]), il est conseillé d'utiliser les fonctions natives strtr / str_replace couplées avec utf8_encode/utf8_decode – encore une fois dans le cas où l'on se limite aux encodages utf-8 et latin1 (ce qui est d'ailleurs est le cas dans 99% des cas).

En effet, les expressions régulières sont bien trop gourmandes par rapport à strtr et encore plus par rapport à str_replace.

Si on passe par strtr il est préférable d'utiliser la forme avec un tableau : str( $str, array( 'à' => 'a' , 'é' => 'e' , etc. ); ce qui évite quelques soucis.

Dans le cas où l'on a effectivement besoin d'encodage « extra », par exemple lorsque l'on travaille sur un site japonais utilisant un encodage comme le EUC-JP, on a pas vraiment le choix que de passer par la méthode htmlentities ou autres méthodes plus complexes encore.

Néanmoins, je me permets une petite remarque, il est plus avantageux de trier les éléments « conditionels » dans une expression régulière :

(?:acute|cedil|circ|grave|ring|tilde|uml)

et non :

(?:uml|circ|tilde|acute|grave|cedil|ring)

En effet, la première version permet un gain de performance (même si certains appuient qu'elle est inutile dans le cadre d'une utilisation simple).

Dans tous les cas, tous ces problèmes seront réglés avec l'arrivée de PHP 6.

Il n'y a plus qu'à attendre…

4 gofromiel a écrit : Mercredi 11 Juin 2008 à 19:30

Merci pour ta contribution monsieur K. mais comme tu peux t'en douter, je ne partage pas vraiment ton avis.

D'abord au sujet de strtr(). Alors oui, on peut s'amuser à recopier la centaine de caractères à traduire, en espérant ne pas en oublier. Après une heure de remplissage on obtient surement une fonction pratique.

MAIS.

  1. Il en faut deux versions (ou plus) : une pour traduire les chaines utf-8 (à placer dans un fichier en utf-8), et une pour traduire les chaines iso-8859-1 (à placer dans un fichier iso-8859-1)…
  2. On a plus la chance de tomber sur un caractère non supporté qu'avec ma fonction (qui vire tout ce qui ne lui plait pas dans la dernière phase).
  3. Impossible d'utiliser la même fonction pour supprimer les accents de chaines aux encodages différents. Ce qui est un jeu d'enfant avec ma fonction puisqu'il suffit de lui indiquer l'encodage source.

Ensuite, cette fonction ne fait que ce qu'elle dit : supprimer les accents des caractères. Ce n'est donc pas une très bonne idée de lui donner une chaine en EUC-J (ou Big5 pour ne pas faire de jaloux).

Quand à l'arrivée de PHP6, cela ne changera rien au schmilblick. Ce n'est pas parce qu'il travaille en utf-8 que l'on aura plus besoin de supprimer des accents de temps à autre (pour les noms de fichiers par exemple).

Bref, je conserve ma jolie fonction qui ne fait que quelques lignes et que je trouve si pratique (et jolie).

Je te remercie pour le conseil d'ordonnancement du masque conditionnel. Si ça peut faire gagner quelques micro secondes ;-)

3 Evan a écrit : Lundi 16 Juin 2008 à 14:17

Bonjour,

je pense aussi que ta solution est trop compliquée et bien trop gourmande. De plus elle est incomplète et suppose que l'encodage est iso-8859-1, ça ne va pas.

La solution complète et la plus rapide est une détection du type d'encodage (mb_detect_string), puis une conversion (iconv), puis un str_replace pour finir.

Pour finir, tu cites l'intégralité des accents des langues à alphabet latin : ÁÀÂÄÃÅÇÉÈÊËÍÏÎÌÑÓÒÔÖÕÚÙÛÜÝáàâäãåçéèêëíìîïñóòôöõúùûüýÿ. C'est tout, il n'yen a pas d'autres. Cette solution est donc complète, je ne comprend pas pourquoi tu parles de « en espérant ne pas oublier de caractère ».

Bonne chance

2 KissGround a écrit : Lundi 16 Juin 2008 à 15:00

Super ! Pour ma part, elle me convient pleinement. L'on peut dire que tu as l'exclu… J'ai eu beau chercher, il n'y a rien de mieux.

KissGround ;)

1 madoxav a écrit : Mardi 01 Juillet 2008 à 11:47

Waouh! Un grand merci, après deux bonnes heures à batailler, j'ai enfin trouvé ce qu'il me fallait ;) bravo encore et bonne continuation!