%CHARCOUNT
Compter les caractères (et non les octets)
Renvoie le nombre de caractères d'une chaîne, là où %LEN compte les octets ou les unités : la différence apparaît dès qu'un texte UTF-8 ou UTF-16 contient un accent ou un emoji.
Syntaxe
%CHARCOUNT(expression)
expression- Une chaîne de caractères, en particulier une zone de CCSID 1208 (UTF-8) ou 1200 (UTF-16). Sur un texte monooctet, le résultat est le même que
%LEN. - Résultat
- Un entier : le nombre de caractères, un caractère de plusieurs octets (UTF-8) ou de deux unités (paire de substitution UTF-16) ne comptant que pour un.
À quoi ça sert
Depuis que les données arrivent du web, de JSON ou d'un fichier UTF-8, une zone peut contenir des caractères sur plusieurs octets. %LEN donne alors une valeur trop grande pour ce que voit l'utilisateur. Pour limiter un libellé à 10 caractères, ou pour remplir une colonne d'affichage, c'est %CHARCOUNT qu'il faut utiliser.
Exemple 1 : UTF-8 : accent et emoji
**free
// %CHARCOUNT : compter les caracteres, pas les octets, d'un texte UTF-8.
ctl-opt dftactgrp(*no) actgrp(*new);
// Les octets UTF-8 sont ecrits en hexadecimal : la source reste sans accent.
dcl-s brutCafe char(5) ccsid(*hex) inz(x'436166C3A9'); // Cafe + e accent aigu
dcl-s brutOk char(6) ccsid(*hex) inz(x'4F4BF09F9880'); // OK + emoji souriant
dcl-s ville varchar(20) inz('Montpellier');
dcl-s cafe varchar(20) ccsid(1208);
dcl-s ok varchar(20) ccsid(1208);
cafe = brutCafe;
ok = brutOk;
snd-msg 'Montpellier : ' + %char(%len(ville)) + ' octets, ' + %char(%charcount(ville)) +
' caracteres';
snd-msg 'Cafe : ' + %char(%len(cafe)) + ' octets, ' + %char(%charcount(cafe)) +
' caracteres';
snd-msg 'OK + emoji : ' + %char(%len(ok)) + ' octets, ' + %char(%charcount(ok)) +
' caracteres';
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
Montpellier : 11 octets, 11 caracteres
Cafe : 5 octets, 4 caracteres
OK + emoji : 6 octets, 3 caracteres
Les octets sont écrits en hexadécimal pour garder une source sans accent. Café occupe 5 octets pour 4 caractères, car le é prend 2 octets. OK suivi d'un emoji souriant fait 6 octets (l'emoji en prend 4) mais 3 caractères. Sur Montpellier, en monooctet, les deux fonctions donnent 11.
Exemple 2 : UTF-16, UCS-2 et contrôle de longueur
**free
// %CHARCOUNT : UTF-16 (CCSID 1200) et UCS-2 (CCSID 13488), et un controle de longueur.
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-s utf16 varucs2(10) ccsid(1200);
dcl-s ucs2 varucs2(10);
// Dix fois la lettre e accent aigu, soit 20 octets en UTF-8.
dcl-s brut char(20) ccsid(*hex) inz(x'C3A9C3A9C3A9C3A9C3A9C3A9C3A9C3A9C3A9C3A9');
dcl-s libelle varchar(30) ccsid(1208);
dcl-c MAX_CAR 10;
// A puis un emoji, ecrits en UTF-16 : l'emoji occupe deux unites (une paire de substitution).
utf16 = u'0041D83DDE00';
ucs2 = u'0041D83DDE00';
snd-msg 'UTF-16 : ' + %char(%len(utf16)) + ' unites, ' + %char(%charcount(utf16)) +
' caracteres';
snd-msg 'UCS-2 : ' + %char(%len(ucs2)) + ' unites, ' + %char(%charcount(ucs2)) +
' caracteres';
libelle = brut;
if %len(libelle) > MAX_CAR;
snd-msg 'Avec %LEN : refuse (' + %char(%len(libelle)) + ' octets)';
endif;
if %charcount(libelle) <= MAX_CAR;
snd-msg 'Avec %CHARCOUNT : accepte (' + %char(%charcount(libelle)) + ' caracteres)';
endif;
*inlr = *on;
Résultat réel, compilé et exécuté sur IBM i 7.5 :
UTF-16 : 3 unites, 2 caracteres
UCS-2 : 3 unites, 3 caracteres
Avec %LEN : refuse (20 octets)
Avec %CHARCOUNT : accepte (10 caracteres)
Le même texte A + emoji, écrit en unités UTF-16, compte 3 unités mais 2 caractères : l'emoji est une paire de substitution. Une zone UCS-2 ordinaire (sans CCSID 1200) ne connaît pas ces paires et compte 3 caractères.
Le contrôle final porte sur dix fois la lettre é, soit 20 octets : %LEN refuse le libellé, %CHARCOUNT l'accepte, ce qui correspond bien à 10 caractères.
Le piège
Le résultat dépend du CCSID de la zone. Sur un varchar UTF-8, %LEN compte des octets (5 pour Café) et %CHARCOUNT des caractères (4). Sur une zone UCS-2 sans CCSID 1200, la paire de substitution d'un emoji compte pour deux caractères, comme le montre le second exemple.