Aller au contenu

%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.