Compter les caractères et les octets en PHP

Le sujet de cet article peut sembler simpliste, mais il ne l’est pas tant que ça, parce qu’il peut engendrer des bugs assez délicats à débusquer.

Le problème

Dans n’importe quel langage de programmation, on se retrouve souvent à vouloir connaître la longueur des données stockées dans une variable. Dans le cas d’une chaîne de caractères ou de données binaires, les choses peuvent parfois être plus délicates qu’on pourrait le croire au premier regard.

La base, c’est la fonction strlen() du C, qui retourne la longueur d’une chaîne de caractère… en comptant qu’un caractère est égal à un octet. Et ça fonctionnait très bien tant qu’on était sur des encodages mono-octet : ASCII, latin1 (ISO-8859-1), latin9 (ISO-8859-15), etc.

Sauf que de nos jours, nous utilisons tous − fort heureusement − l’Unicode. Et si vous êtes à peu près sain d’esprit, vos fichiers sont encodés en UTF-8, qui est un encodage particulièrement intelligent (compatible avec l’ASCII pour les caractères latins de base). Il faut donc avoir en tête que les caractères ainsi encodés peuvent prendre de un à quatre octets.
Donc le nombre de caractères et le nombre d’octets ne sont pas du tout la même chose.

En PHP

Si on revient au PHP, on peut voir assez souvent du code qui a besoin de connaître la longueur d’une chaîne de caractères, et qui fait quelque chose de ce genre :

$taille = strlen($chaine);Langage du code : PHP (php)

Sauf que le problème, évidemment, c’est que la fonction strlen() de PHP est comme celle du C : elle ne retourne pas le nombre de caractères, mais le nombre d’octets de la chaîne.

$chaine = 'héhé';
$taille = strlen($chaine); // = 6Langage du code : PHP (php)

La solution, c’est d’utiliser la fonction mb_strlen(). Comme son nom l’indique, elle gère les caractères multioctets (“mb” pour “multibyte”). On l’utilisera en spécifiant l’encodage utilisé en deuxième paramètre :

$chaine = 'héhé';
$taille = mb_strlen($chaine, 'UTF-8'); // = 4Langage du code : PHP (php)

Comme la plupart des systèmes sont aujourd’hui nativement en UTF-8, vous pouvez même simplement écrire :

$chaine = 'héhé';
$taille = mb_strlen($chaine); // = 4Langage du code : PHP (php)

Très bien, mais dans les cas où on veut connaître le nombre d’octets, et non pas le nombre de caractères (par exemple si on a des données binaires, ou bien si on veut s’assurer qu’on va pouvoir stocker la chaîne dans un espace donné), comment faire ?

On pourrait se dire que la fonction strlen() est justement là pour ça. Et c’est vrai que ça fonctionne très bien d’un point de vue purement technique.
Par contre, pour la relecture de code, c’est une horreur. À chaque fois que je vois un strlen(), je me demande si le développeur voulait réellement compter le nombre d’octets, ou s’il ne connaît simplement pas la subtilité et qu’il est allé au plus simple.

Donc pour compter le nombre d’octets, on va utiliser mb_strlen() en lui disant d’utiliser le code ASCII, pour qu’il compte un caractère par octet :

$chaine = 'héhé';
$taille = mb_strlen($chaine, 'ASCII'); // = 6Langage du code : PHP (php)

Donc comme ça les choses sont claires : dans tous les cas il faut utiliser mb_strlen(), et l’encodage passé en paramètre permet de connaître précisément l’intention du développeur :

  • UTF-8 (ou pas d’encodage spécifié) pour compter les caractères
  • ASCII pour compter les octets

Et ailleurs ?

Pour info, le PHP n’est pas le seul langage qui demande un peu de gymnastique.

En Python, pour obtenir le nombre de caractères dans une chaîne, il faut faire :

taille = len(chaine)Langage du code : Python (python)

Pour obtenir le nombre d’octets, il faut réencoder la chaîne, ce qui fait des traitements supplémentaires :

taille = len(chaine.encode('utf-8'))Langage du code : Python (python)

En Javascript, pour obtenir le nombre de caractères :

taille = chaine.length;Langage du code : JavaScript (javascript)

Pour obtenir le nombre d’octets, il faut là encore réencoder la chaîne :

taille = (new TextEncoder().encode(chaine)).length;Langage du code : JavaScript (javascript)

Le Ruby offre une gestion native par la classe String, tant que l’encodage par défaut est défini correctement.
Pour récupérer le nombre de caractères :

taille = chaine.lengthLangage du code : Ruby (ruby)

Pour avoir le nombre d’octets :

taille = chaine.bytesizeLangage du code : Ruby (ruby)

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.