PHP / MySQL : comment résoudre les problèmes d’accents ?

Introduction

Le jeu de caractères est un point très important à connaître avant même d’écrire le premier bout de code d’un site web. Si vous négligez cette partie intégrante du développement, alors vous courrez à des risques d’incompatibilité qui se traduirons par l’apparition de caractères ressemblant à cela : é, �.

Nous allons voir dans cet article les points les plus importants à connaître pour partir du bon pied dans le développement de votre projet.

Les jeux de caractères : courte explication

Pour un ordinateur, les caractères ne signifient strictement rien. Il ne comprend que les nombres. Afin de gérer les caractères, une table de correspondance appelée ASCII a été mise au point. Grâce à l’ASCII, l’ordinateur peut gérer jusqu’à 128 caractères, permettant d’écrire en anglais (par exemple, le caractère « A » correspond au nombre 65).

Toutefois, les 128 caractères ne permettaient pas la gestion de la totalité des langues existantes. Différents jeux de caractères ont alors été créés par la suite. Aujourd’hui, nous utilisons principalement l’ISO 8859-15 et l’UTF-8 (Unicode).

L’ISO 8859-15 permet d’écrire dans l’alphabet latin, c’est-à-dire en français, anglais, allemand, etc. L’UTF-8 permet la gestion de bien plus de langues (ex : caractères asiatiques).

Il faut savoir que l’ISO 8859-15 et l’UTF-8 ne sont pas compatibles, leur structure étant différente. Lorsque vous voyez des caractères étranges ou encore des signes s’apparentant à des points d’interrogation, c’est que vous mélangez probablement plusieurs jeux de caractères (ex : ISO et UTF-8).

Personnellement, j’utilise systématiquement l’UTF-8 dans mes projets et ce même si seul le français est utilisé. Ainsi, les projets pourront, à long terme, être traduits vers d’autres langues si nécessaire et sans souci.

Procédure à suivre

Il faut que tout l’environnement de votre projet soit dans le même jeu de caractères. Ceci comprend :

  1. les fichiers
  2. l’HTML
  3. le PHP
  4. et enfin MySQL (ou autre SGBD)

J’utiliserai l’UTF-8 comme jeu de caractères, mais le principe est le même pour les autres jeux. Quand j’indiquerai « ISO », je ferai référence à l’ISO 8859-15 (c’est juste un point de comparaison).

Les fichiers

La première étape est d’écrire les fichiers en UTF-8. Pourquoi ? Imaginons que vous souhaitez par exemple écrire un caractère asiatique dans le fichier. Si vous enregistrez le fichier en ISO, vous aurez alors des erreurs. Par exemple, dans Geany, une erreur s’affiche quand j’enregistre un fichier ISO contenant un caractère non pris en charge :

En rouge, j’ai souligné l’indicateur du jeu de caractères utilisé. Il est très souvent indiqué dans la barre de statut.

Il est possible de le changer en allant dans le menu. Dans cet éditeur de texte, c’est « Document > Définir l’encodage > Liste d’encodages ».

Je place donc le jeu de caractères sur UTF-8, et le contenu du fichier sera donc écrit en UTF-8.

Mise en garde : il faut bien vérifier que vous utilisez UTF-8 sans BOM. Le BOM est un marqueur d’ordre d’octets, permettant de détecter l’encodage Unicode du fichier. PHP ne gérant pas le BOM, ce dernier est interprété comme un caractère, donc un contenu hors des balises PHP en début de fichier. Vous aurez donc droit à des erreurs de type « header already sent by » lors de l’utilisation de fonction comme « session_start » ou encore « header« .

L’HTML

C’est la partie la plus simple. Elle consiste à indiquer au navigateur dans quel jeu de caractères la page est écrite. Une simple balise « meta » suffit :

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Le PHP

PHP n’a pas été développé avec le support natif d’Unicode, mais en ISO. Ce qui signifie que certaines fonctions peuvent poser problème avec l’encodage UTF-8. Si vous choisissez l’ISO, vous n’aurez pas ce souci.

Pour illustrer ce fameux problème, voici un exemple :

echo strlen('testé');

En théorie, le résultat de cette fonction devrait être 5, et pourtant, si vous testez ce code vous remarquerez que le résultat retourné est 6. PHP traitant la chaîne de caractères en ISO, il compte deux octets pour le caractère « é » ce qui porte la longueur de la chaîne à 6. Pratiquement toutes les fonctions sont concernées (strtoupper, strtolower, strstr, etc.). C’est une vrai problématique.

Heureusement, une extension appelée « mbstring » ajoute de nouvelles fonctions pour le traitement de chaîne de caractères dans différents jeux de caractères. Pour trouver les correspondances avec les anciennes fonctions, il suffit en général d’ajouter le préfixe « mb_ » et de préciser le jeu de caractères que vous souhaitez utiliser.

Voici un exemple d’utilisation :

// fonction natif, renvoi 6
echo strlen('testé');

// fonction mbstring, renvoi 5 en précisant l'encodage utilisé
echo mb_strlen('testé', 'UTF-8');

La fonction « mb_internal_encoding » permet de définir le jeu de caractères à utiliser par défaut, comme ceci :

// défini l'UTF-8 comme encodage par défaut (à placer dans le fichier de configuration par exemple)
mb_internal_encoding('UTF-8');

// inutile de préciser l'encodage
echo mb_strlen('testé');

Il devient donc inutile de préciser le jeu de caractères en second paramètre.

Parfois, il vous sera nécessaire de traiter une chaîne de caractères dans un jeu différent de celui utilisé dans votre projet. C’est le cas lorsque vous récupérez des informations à distance, sur un autre site par exemple. Dans ce cas, il vous faudra convertir la chaîne de caractères récupérée dans le bon jeu.

Deux fonctions existent et peuvent être utiles : utf8_encode et utf8_decode.

Elles permettent de convertir des chaînes de l’ISO vers UTF-8 et inversement. utf8_encode convertit une chaîne ISO-8859-1 en UTF-8, et utf8_decode effectue l’inverse. Référencez-vous à la documentation de ces deux fonctions pour en savoir plus.

Comme seul l’ISO-8859-1 est pris en charge, le caractère euro ne pourra être converti, car il n’est présent que dans l’ISO-8859-15. Quelques autres caractères sont concernés.

Une autre solution existe : iconv. C’est une extension permettant la conversion de chaîne de caractères dans différent jeu de caractères. Voici un exemple de conversion :

setlocale(LC_CTYPE, 'fr_FR.UTF-8');
echo iconv('UTF-8', 'ISO-8859-15', '@ € ä ç'); // retourne : '@ € ä ç' converti en ISO-8859-15

On précise le jeu de caractères de la chaîne à convertir (UTF-8), ensuite le jeu dans lequel nous souhaitons convertir la chaîne (ISO-8859-15) et enfin la chaîne à convertir.

Cette fonction est particulièrement utile car elle permet de gérer les caractères exotiques. Lorsque nous avons utilisé utf8_decode, le caractère euro (€) s’est transformé en un point d’interrogation, car celui-ci n’est pas supporté par l’ISO-8859-1.

Si nous tentons de convertir la chaîne de caractères précédente en ISO-8859-1 avec iconv, voici ce qui en ressort :

setlocale(LC_CTYPE, 'fr_FR.UTF-8');
echo iconv('UTF-8', 'ISO-8859-1', '@ € ä ç'); // retourne : @ ¤ ä ç

On voit donc que la conversion a échouée sur le signe euro. Mais en ajoutant « //TRANSLIT » :

setlocale(LC_CTYPE, 'fr_FR.UTF-8');
echo iconv('UTF-8', 'ISO-8859-1//TRANSLIT', '@ € ä ç'); // retourne : @ EUR ä ç

iconv a donc converti « € » en « EURO ». Dans le même principe, iconv est particulièrement utile pour supprimer les caractères accentués des chaînes de caractères.

MySQL

Comme à chaque fois où il est question de chaîne de caractères, il est aussi question de jeu de caractères, et MySQL n’échappe pas à la règle.

Que ce soit la création de la base de données, la création de table, des champs ou même la connexion au serveur, il est possible dans chacun de ces points de spécifier le jeu de caractères à utiliser.

Tout d’abord, il faut indiquer lors de la connexion au SGBD le jeu de caractères que nous souhaitons utiliser. Pour ce faire, une simple requête SQL suffit :

SET NAMES 'utf8';

Cette requête est la première à effectuer avant toute autre requête. Elle indique que les chaînes de caractères à traiter dans les prochaines requêtes seront en UTF-8.

Ensuite, nous créons une base de données avec le jeu de caractères UTF-8 :

CREATE DATABASE NomDeLaBase
CHARACTER SET utf8
COLLATE utf8_bin;

« COLLATE » défini la collation à utiliser. J’utilise personnellement les deux suivantes :

  • utf8_bin : la chaîne de caractères sera sensible à la casse. Donc le caractère « e » sera considéré comme différent de « E ».
  • utf8_general_ci : la chaîne sera insensible à la casse. Donc « e » sera considéré équivalent à « E », mais en plus de cela « e » sera équivalent à « é ». Pratique pour la recherche.

Lors de la création de table, les jeux de caractères peuvent aussi intervenir. Par exemple :

CREATE TABLE Users (
	id MEDIUMINT UNSIGNED AUTO_INCREMENT,
	username VARCHAR(30) NOT NULL,
	mail VARCHAR(60) NOT NULL UNIQUE COLLATE utf8_general_ci,
	password VARCHAR(40) NOT NULL,
	PRIMARY KEY(id)
) ENGINE=INNODB CHARACTER SET utf8 COLLATE utf8_bin;

Ici nous spécifions le jeu à utiliser globalement à la table (ligne 7). Mais, pour le champ « mail », nous définissons une collation différente pour que le champ soit insensible à la casse.

C’est à peu près tout pour MySQL.

Conclusion

Les jeux de caractères sont loin d’être aussi compliqués à comprendre et à utiliser. Tout est question d’attention. La seule complexité est, selon moi, l’utilisation avec PHP du fait de ses fonctions natives ne supportant pas UTF-8. Mais ce problème franchi avec les bon outils (iconv et mbstring), c’est presque un jeu d’enfant de programmer correctement sans souci de jeu de caractères.

NOTE : si vous rencontrez des erreurs dans cet article, n’hésitez pas à me les signaler, merci.

65 réflexions sur « PHP / MySQL : comment résoudre les problèmes d’accents ? »

  1. Ping : Tout savoir pour bien gérer les accents dans un site web | KubX

  2. Ping : Tweets that mention PHP / MySQL : comment résoudre les problèmes d’accents ? | Programmation web -- Topsy.com

  3. Je n’utilise plus du tout la fonction « header » pour définir le type d’encodage.
    Normalement, avec les explications de cet article, il est inutile de l’utiliser.

  4. Merci pour ce tuto très bien fait.
    J’ai respecté scrupuleusement les instruction et quelque chose m’interpelle…
    Quand je me connecte avec PhpMyAdmin, ce que je vois des données enregistrées, elles ne correspondent pas du tout à ce que je vois avec mes formulaires qui eux fonctionnent parfaitement grâce à ce tuto.
    Exemple, j’enregistre dans un formulaire « ç ñ é à ». A la lecture des données de la base j’ai bien « ç ñ é à », donc tout baigne… Sauf que sous PhpMyAdmin il m’affiche les données suivantes « c383c2a720c383c2b120c383c2a920c383c2a0 »
    Et quand j’édite les champs toujours avec PhpMyAdmin : « Ã§ ñ é à »
    Est-ce mon PhpMyAdmin qui n’est pas en UTF8 ?

  5. « c383c2a720c383c2b120c383c2a920c383c2a0″ semble être la valeur hexadécimale du champ. Cela m’arrive aussi parfois, je n’ai pas vraiment cherché le pourquoi.

    Pour le problème d’encodage par contre, si tout le reste est correctement configuré, alors cela doit provenir de phpMyAdmin qui n’est pas dans le bon encodage. La modification de valeur par phpMyAdmin provoquera alors des problèmes lors de l’affichage sur ton site.

    Pour vérifier l’encodage, tu devrais avoir l’information sur la page d’accueil de phpMyAdmin (où il y a une liste des différents paramètres du serveur MySQL).
    Par exemple, chez moi, j’ai :
     » Interclassement pour la connexion MySQL  » suivis d’une liste déroulante contenant la valeur actuel (utf8_general_ci).

    Tiens moi au courant.

  6. Ping : J’ai des ? à la place des caractères accentués…

  7. pour moi les voyeles avec accents ne sont pas affichées du tout et le text qui vient après n’est pas affiché aussi !
    je ne sais vraiment quoi faire

  8. Un grand merci pour ce résumé qui m’a bien servi.

    Bien vu pour mbstring et la longueur des chaînes de caractère, c’est un piège sournois à connaître.

    J’ajouterai juste pour ceux qui se demandent comment mettre en place le « SET NAMES UTF8 » côté PHP, il faut simplement une ligne du style :
    $req = mysqli_query($conn, « SET NAMES UTF8 »);

    • Pour le HTML il suffit d’écrire <meta charset= »utf-8″>, ça suffit (pas besoin de <meta http-equiv= »Content-Type » content= »text/html; charset=utf-8″ />) en HTML5 en tout cas.

      • define(« HOST », « localhost:3306 »);
        define(« USER », « db_utilisateur »);
        define(« PASSWORD », « motdepasse »);
        define(« DATABASE », « nombase »);

        $mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);

        mysqli_set_charset($mysqli, ‘utf8’); // MYSQLI CHARSET UTF-8

        $results = mysqli_query($mysqli, « SELECT * FROM matable WHERE ma=’condition' »);

        while ($response= mysqli_fetch_assoc($results))
        {
        echo « $response[‘ma_colone’] »;
        }

  9. Je démarre un projet avec WAMP et je conserve un problème d’affichage des caractères accentués. Ainsi, le caractère « è » figurant dans un champ de ma base de données est affiché « ? » par mon navigateur (Chrome). Il me semble avoir suivi les consignes correctement. Cependant, phpMyAdmin refuse de mettre UTF8_general_ci à  » l’interclassement pour la connexion au serveur  » (il convertit ma sélection en UTF8mb4_general_ci). La source de mon problème ?

  10. Bonjour,

    Moi, mon problème est que lorsque je fait un copie/coller d’un texte ou d’un adresse mail de mon site vers un éditeur de texte (type word), je perd certain caractère et d’autre sont changé. Je perd le « point » « @ » ect…

    Quelque peut m’explique pourquoi?

    Je vous remercie d’avance.

  11. Bonjour à tous ,c’est bien gentil tout çà,mais pour un novice,comment supprimmer ces caractères intempestifs,
    quelles procèdure facile et comppréhensible
    cordialement

  12. Très bon article.
    J’ai cependant eu des problèmes pour lire un fichier en php avec des enregistrements contenant des caractères avec des accents. cela a été résolu en utilisant iconv:
    if(!($myFile = utf8_fopen_read($filename, « r »))) {
    print(‘Importation1 Erreur 1 ‘);
    exit();
    }
    avec
    function utf8_fopen_read($fileName) {
    $fc = iconv(‘ISO-8859-1’, ‘utf-8’, file_get_contents($fileName));
    $handle=fopen(« php://memory », « rw »);
    fwrite($handle, $fc);
    fseek($handle, 0);
    return $handle;
    }

  13. Voici pourquoi je préfère les sites anglophones: Tu as écrit un journal pou ne pas dire explicitement comment résoudre le problèmes d’accents.

    • Il n’est pas nécessaire d’être désagréable. Le mot « éviter » aurait peut être été plus judicieux que le mot « résoudre » mais, dans le fond, cet article doit amener à la résolution du problème. Il y a quatre points important, il n’est pas possible de faire un tutoriel pour chaque cas. Tu as un problème d’accent ? Reprend chaque point et fait les vérifications décrites. Si c’est juste pour ramener ta science sur l’utilisation d’un mot, passe ton chemin car ton commentaire ne sert strictement à rien.

      Enfin, si tu préfères l’anglais, je ne te retiens pas. J’ai écrit cet article pour ceux à qui l’anglais peut poser problème, ce qui ne semble pas te concerner. Alors, que fais-tu ici ?

  14. Bonjour
    Ce tutoriel m’a beaucoup aidé.
    J’ai bien compris les effets de l’incompatibilité des jeux de caractères mais néanmoins je ne m’explique pas tout même si j’ai résolu mes problèmes d’accentuation des caractères.
    Merci et poursuivez vos tutoriels.

  15. Bonjour et milles merci pour ce fameux et utile tutoriel sur les accents.
    J’ai fait le choix de mettre tout en iso et cela fonctionne assez bien je dit assez car il arrive que quelque fois les accents ne s’affiche pas comme il faut.

    Je n’arrive pas à comprendre d’où viens le problème. C’est un simple formulaire html rempli par les internautes et selon l’internaute les accents s’affichent correctement et se range correctement dans la base sql et quelque fois je ne sais pas pourquoi ça coince… Je me casse la tête depuis des semaines pour trouver d’où ça vient mais j’y arrive pas…

  16. Re bonjour, mon écriture (du début)

    drop table `fete_names_tbl`

    CREATE TABLE IF NOT EXISTS `fete_names_tbl` (
    `id` int(4) NOT NULL,
    `month` decimal(2,0) NOT NULL,
    `day` decimal(2,0) NOT NULL,
    `celebration` text NOT NULL COLLATE utf8_general_ci,
    `type` decimal(1,0) NOT NULL,
    `present` decimal(1,0) NOT NULL
    ) ENGINE=INNODB CHARACTER SET utf8 COLLATE utf8_bin;

    $link = mysqli_connect($serveur, $login, $password, $base);
    $query = sprintf(« SELECT celebration,present,type FROM fete_names_tbl WHERE day = $d and month = $m order by type DESC, celebration ASC »);
    $result = mysqli_query($link,$query);
    if (!$result) {
    $message = ‘Requ? invalide : ‘ . mysql_error() . « \n »;
    $message .= ‘Requ? compl? : ‘ . $query;
    die($message);
    }

    while ($row = mysqli_fetch_assoc($result)) {
    $fete = $row[‘celebration’];
    $feteoui = $row[‘present’];
    $typeun = $row[‘type’];

    résultat:
    Ga�tan
    Une correction me serait bien venue
    Salutations

  17. Les � persistent chez moi 🙁

    J’importe d’abord un fichier csv (encodé en UTF-8) dans phpmyadmin.
    Champs encodés en utf8_general_ci, les accents apparaissent bien.

    Pour traiter et visualiser mes données, je passe par un PHP encodé en UTF-8 sans BOM
    J’ai testé : mysqli_set_charset($mysqli, ‘utf8’);
    J’ai mis :
    utf8_encode ou utf8_decode n’y changent rien.

    Si je fais un simple echo avec du texte avec accent, pas de souci.
    C’est uniquement dans l’affichage d’une donnée.
    Une idée ?

  18. Bonjour, j’ai essayé pas mal de choses mais j’ai toujours les ? à la place des accents.
    Dans ma base sql (chaque champs est en UTF-8) les accents y sont mais dans mes pages php ça bug !
    Dans la méta j’ai préciser charset = UTF-8…
    Je désespère que faut-il que je fasse pour résoudre mon problème !!
    Merci

    • Au niveau de la connexion à la base de données, vous avez bien précisé le charset à utiliser ? (mysqli : $mysqli->set_charset(« utf8 ») )

  19. Bonsoir,
    Merci pour la réponse rapide.
    En fait j’ai un fichier php dans lequel j’ai mes paramètres de connexion à la base sql. Est-ce ce fichier que je dois modifier ? Et comment ? voici ce qu’il y a dedans :

  20. Bonjour,
    je me connecte à la base données avec cette ligne : $DISQUES=mysql_pconnect($Host, $Username, $Password) je rajoute en dessous la ligne $mysql_pconnect->set_charset(‘utf8’) mais c’est toujours pareil !
    Merci pour votre aide…

    • Si c’est mysql_pconnect, ca va être différent :
      $DISQUES=mysql_pconnect($Host, $Username, $Password);
      mysql_query("SET NAMES 'utf8'", $DISQUES);

  21. Bonjour,

    Existe t-il un format de chaîne de caractère qui est sensible aux accents ?

    Je cherche à sortir toutes les lignes qui ont un accent dans ma table en utilisant COLLATE mais je ne trouve pas de format qui me permet de faire ressortir les accents..

    • Voilà la solution pour ton affichage en français
      <?php
      mb_internal_encoding('UTF-8');
      //dictio.txt c'est mon dictionnaire écrit en français
      $mondico = fopen("dictio.txt", "r") or die("incapable d'ouvrir le dictionnaire!");
      // sortir toutes les lignes du dictionnaire jusqu'a la fin
      while(!feof($mondico)) {
      $phrase=fgets($mondico) . " »;
      $phrasea=iconv(« ISO-8859-1″, »UTF-8 »,$phrase);
      echo $phrasea;
      }
      fclose($mondico);
      //et ça marche
      ?>

  22. Pour modifier la base, de façon à ne plus être gêné par ces problèmes d’accents
    Est-il possible de modifier une base existante comme ceci :
    ALTER DATABASE machintruc
    CHARACTER SET utf8
    COLLATE utf8_general_ci;

  23. Bonjour,
    Je suis confronté à un cas bizarre. Mes tables en ligne sont encodées en ISO apparemment: ex: é (au lieu de é) Le champ est noté latin1_swedish_ci et l’interclassement pareil. Sur mes pages html, le contenu des champs apparaît très bien en html.

    J’ai exporté les tables en ligne et les ai importé en local. Donc, elles sont pareilles.

    Mais en local, problème, le contenu des champs sur les pages html apparaît en ISO é (au lieu de é). J’ai essayé utf8_encode, ça marche pas.

    Mes fichiers de connexions pdo sont pareils.

      • Question de paramétrage au niveau du serveur, oui certainement.
        Problème résolu, avais-je écrit… sauf que je m’aperçois que le oe entrelacé n’est pas traduit par decode (probablement aussi le signe euro, mais j’ai pas vérifié. j’ai essayé icov, ça fonctionne pas.

        Dans la table directement le oe entrelacé est noté Å“
        J’ai essayé un str-replace: rien à faire.
        Dans le fichier php il y a un point d’interrogation. Le oe entrelacé, je peux le rajouter sans problème directement. Mon problème est de faire disparaître ce point d’iterrogation. Il devrait bien pouvoir exister une commande php pour faire disparaître ce point d’interrogation.

        • La première chose à faire c’est de ne rien toucher au niveau du contenu.
          Au niveau de la base (phpMyAdmin je suppose ?), il faut regarder si la connexion de phpMyAdmin avec le serveur se fait dans le même interclassement que le charset de la table.
          Les utf8_decode et utf8_encode gèrent la transformation entre UTF-8 et ISO-8859-1 or certains caractères spéciaux comme le € n’en font pas partie et sont dans ISO-8859-15.
          Il faut extraire la base de donnée ouvrir le SQL dans un éditeur de texte comme Geany qui permet ensuite de convertir l’encodage.

  24. Bonjour à tous !
    Pour ne plus jamais avoir de soucis selon les tables utilisées, j’ai mis en place ce code :
    include (‘../../cgi-bin/acces.inc.php’); // le fichier acces.inc.php est composé des données d’accès à la base MySql
    try {
    // set the PDO error mode to exception
    $options = array(
    PDO::MYSQL_ATTR_INIT_COMMAND => ‘SET NAMES utf8mb4’,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC
    );
    $conn = new PDO(« mysql:host=$servername; dbname=$dbname; charset=utf8mb4 », $username, $password,$options);
    // ********************* ci-dessous le travail qu’on veut faire

    vous notez le « utf8mb4 » qui permet de lire les lettres accentuées et tout un tas de bazars même pas toujours connus ! 😉

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Notifiez-moi des commentaires à venir via email. Vous pouvez aussi vous abonner sans commenter.