Formatez vos chaines de caractères grâce à la fonction format

Formatez vos chaines de caractères grâce à la fonction format

Description

Beaucoup de monde, débutants comme initiés, connaissent peu ou mal la fonction Format.

La fonction Format permet tout simplement de formater une chaine de caractères grâce à des spécificateurs de formatage.
Dans cette définition barbare, il faut comprendre que format est une fonction spécialisée dans la conversion de différents types (réel, entier, pointeurs etc.) dans un texte humainement lisible.

Elle remplacera donc parfaitement des fonctions tel que IntToStr, FloatToStr, IntToHex, que certains aiment mettre de part et d'autre dans leurs méthodes de conversions et qui se soucient un minimum de l'optimisation et la clarté du code de ces dernières.

Quand utiliser Format ?

Utilisez format dés que vous désirez convertir une valeur en une chaine de caractères.
En effet, Format va vous faciliter grandement la vie dans ce domaine, en remplaçant toutes les autres fonctions comme FloatToStr ou IntToStr.

Exemple
Afficher les coordonnées de la souris sur le titre de la fiche en cours :

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
begin
    caption := 'x: '+IntToStr(X)+' | y: '+IntToStr(Y);
end;

Sera facilement remplaçable par :

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
begin
    caption := format('x: %d | y: %d',[X, Y]);
end;

Comme vous l'avez compris, Format est LA fonction spécialisée dans le formatage valeurs vers chaine.

Recommandations

- Les chaines de formatage ne sont pas compilées dans Delphi, contrairement en C et C++. Vérifiez donc bien le type des spécificateurs de format que vous utiliserez pour éviter les erreurs d'exécution.

- Pour les concaténations de chaines courtes (string et char), il est préférable de ne pas utiliser format, mais plutôt la bonne vieille méthode qui est la plus rapide, par exemple :

SC := protocol+'://'+domainename+'/'; 
{au lieu de : SC := format('%s://%s/',[protocol,domainename]); }

Performances

Je vous le donne en mille, Format est plus rapide que la plupart des autres méthodes.
Parlons chiffres et millisecondes, avec un bench (test de performance) tout simple :
[test réalisé sur un Athlon 1800+ à 1.5Ghz en parfaite condition de test]

- 1er test :

for X := 1 to 1000000 do s := 'X = ' + IntToStr(X);

Résultat : Moyenne de 945 ms, presque 1 seconde.

- 2eme test :

for X := 1 to 1000000 do s := format('X = %d',[X]);

Résultat : Moyenne de 532 ms, presque 1/2 seconde.

J'ai refait ce test plusieurs fois pour être sur et certain de ce que j'avance maintenant.

Format affiche de meilleures performances et vous permettra d'optimiser correctement vos programmes, surtout sur les méthodes de conversion et d'affichage de texte.

Déclaration de Format dans Delphi

Format est déclarée dans l'unité SysUtils de Delphi.

Format fait appel à FmtStr(); incluse elle aussi dans l'unité SysUtils.

Elle se présente de cette manière :

function Format(const Format: string; const Args: array of const): string;
begin
    FmtStr(Result, Format, Args);
end;

function Format(const Format: string; const Args: array of const;const FormatSettings: TFormatSettings): string;
begin
    FmtStr(Result, Format, Args, FormatSettings);
end;
  • const Format correspond à la chaine de format, elle contient le texte et les spécificateurs de format de cette dernière.
  • const Args correspond au tableau d'arguments à appliquer aux spécificateurs de format contenus dans la chaine de formatage.
  • const FormatSettings correspond aux paramètres des variables globales.

Qu'est-ce qu'une chaine de formatage ?

Une chaine de formatage est tout simplement une chaine qui contient d'une part du texte et d'autre part des spécificateurs de format.

==Qu'est-ce qu'un spécificateur de format ?

Un spécificateur de format est un "marqueur" qui permet d'identifier le type et les paramètres de traitement d'un argument à formater dans une chaine de format.

Dans Delphi comme dans C++, un spécificateur de format commence toujours par le symbole "%". Il est donc logique que le caractère % (pour afficher un pourcentage par exemple) s'obtient donc en le doublant comme ceci "%%" (a l'instar d'un double ' ' dans une chaine string).

Exemple :

format('le rapport est de : %.2f %%',[rapport]);

Les spécificateurs de format ont la forme suivante : (tiré de l'aide Delphi)

"%"[index ":"]["-"][largeur]["."prec]type

Comme dit plus haut, Le spécificateur de format commence toujours par le symbole %,
il sera suivi ensuite de divers paramètres (dans l'ordre) :

  • [index ":"] correspond à l'index de l'argument dans le tableau d'arguments (facultatif)
  • ["-"] correspond à l'alignement par défaut à gauche (facultatif)
  • [largeur] correspond à la taille (facultatif)
  • ["." prec] correspond à la précision (facultatif)
  • type correspond au type de l'argument à convertir (obligatoire) (Provoquera une erreur de conversion si l'argument n'est pas de ce type)

Index, Largeur et Précision doivent être de type entier.

Tableau des types de spécificateurs de format

%d

Entiers signés.

Types supportés : integer, smallint, shortint, longint, byte, word, int64, cardinal.

Si le spécificateur de précision est défini, la chaine en sortie sera remplie de zéro, si la taille de l'entier est inférieure à celle de la précision.

Exemple :

format('mon chiffre est : %.4d', [12]);

Renvoie "mon chiffre est : 0012"

%u

Entiers non-signés. (valeur absolue)

Identique à %d mais sans retour de signe.

%u est équivalant à :

format( '%d', [abs(var)]);

%e

Réel signé. (écriture scientifique)

Types supportés : single, real, real48, double, comp, extended, currency.

La valeur est convertie en une chaîne de la forme "-d.ddd...E+ddd".

Le nombre total de chiffres dans la chaîne résultante (y compris celui qui précède la virgule) est donné par le spécificateur de précision dans la chaîne de format. Si celui-ci est omis, une précision de 15 est prise en compte par défaut.

Le caractère "E" dans la chaîne résultante est toujours suivi d'un signe "+" ou "-",
puis de trois chiffres au moins.

%f

Réel signé.

Types supportés : single, real, real48, double, comp, extended, currency.

Définissez un spécificateur de précision pour définir le nombre de chiffres après la virgule.

Exemples :

format('Pi = %.4f', [pi]); 

Renvoie "Pi = 3,1415"

format('Pi = %.0f', [pi]);  

Renvoie "Pi = 3"

%g

Réel signé. (au plus court)

Types supportés : single, real, real48, double, comp, extended, currency.

La valeur est convertie en une chaîne décimale la plus courte possible en utilisant le format fixe ou scientifique.

Le nombre de chiffres significatifs dans la chaîne résultante est fourni par le spécificateur de précision dans la chaîne de format : une précision par défaut de 15 est prise en compte si le spécificateur de précision est omis.

Les caractères zéro sont supprimés de la fin de la chaîne résultante et la virgule décimale n'apparaît que si elle est nécessaire.

La chaîne résultante utilise le format fixe si le nombre de chiffres à gauche de la virgule est inférieur ou égal à la précision indiquée et si la valeur est supérieure ou égale à 0,00001. Sinon, la chaîne résultante fait appel au format scientifique.

%n

Réel signé. (avec séparateur des milliers)
Entier signé. (voir la note)

Types supportés : single, real, real48, double, comp, extended, currency.

Numérique. L'argument doit être une valeur flottante. La valeur est convertie en une chaîne de la forme"-d,ddd,ddd.ddd...". Le format "n" correspond au format "f", sauf que la chaîne résultante contient le séparateur des milliers.

Exemple :

format('%n', [10248.125]);

Renvoie "10 248,125"

NOTE

Types : integer, smallint, shortint, longint, byte, word, int64, cardinal.

Pour ces types vous devrez écrire format de cette façon :

format('%.0n', [VarInt / 1]);

Exemple :

format('Taille : %.0n octets', [24533126/1]);

Renvoie "Taille : 24 533 126 octets"

%m

Réel signé. (représentation monétaire)

Types supportés : single, real, real48, double, comp, extended, currency.

La valeur est convertie en une chaîne représentant une valeur monétaire.

La conversion est contrôlée par les variables globales CurrencyString, CurrencyFormat, NegCurrFormat,ThousandSeparator, DecimalSeparator et CurrencyDecimals ou leur équivalent dans une structure de données TFormatSettings.

Si la chaîne de format contient un spécificateur de précision, il remplace la valeur envoyée par la variable globaleCurrencyDecimals ou son équivalent TFormatSettings.

Exemples :

CurrencyString := '€';
format('Francs en Euros : %.5m', [100 / 6.55957]);

Renvoie "Francs en Euros : 15,24490 €"

CurrencyString := 'Fr';
format('Euros en Francs : %.2m', [15.24490 * 6.55957]);

Renvoie "Euros en Francs : 100,00 Fr"

%p

Pointeur.

Types supportés : (^Type), PAnsiString, PString,PByteArray, PCurrency, PDouble, PExtended, PSingle, Pinteger,POleVariant, PShortString, PTextBuf, PVarRec, PVariant, PWideString, PWordArray.

La valeur est convertie en une chaîne de 8 caractères (32 bits) qui représente des valeurs de pointeur en hexadécimal.

Exemple :

format('%p', [PInteger(1024)]);

Renvoie "00000400"

%s

Chaîne.

Types supportés : String, Char, PChar, AnsiString, WideString, ShortString.

La chaîne ou le caractère est inséré à la place du spécificateur de format.

Définissez le spécificateur de précision pour indiquer la taille maximale de la chaine, qui sera alors tronquée automatiquement si elle est plus grande.

Exemple :

format('nom : %s, prénom : %s', [edit1.text, edit2.text]);

Renvoie "nom : Borland, prénom : Delphi"

%x

Hexadécimal.

Types supportés : integer, smallint, shortint, longint, byte, word, int64, cardinal.

La valeur est convertie en sa représentation hexadécimale.

Si la chaîne de format contient un spécificateur de précision, ce dernier spécifie que la chaîne doit contenir au moins le nombre indiqué de chiffres; si cela n'est pas le cas, des caractères zéro de remplissage sont rajoutés dans la partie gauche de la chaîne.

Exemple :

format('Char : #%0:.3d | Hexa : %0:.2x', [ord(' ')]);

Renvoie "Char : #032 | Hexa : 20"

Format flottant et séparateur décimal

"Quelque soit le format flottant, les deux caractères utilisés comme séparateur décimal et séparateur des milliers sont respectivement définis par les variables globales DecimalSeparator et ThousandSeparator ou leur équivalent TFormatSettings."

Il faut comprendre par là, qu'en assignant une nouvelle valeur à ces variables globales, on peut modifier l'aspect d'affichage mais également de réceptions des valeurs de type flottant.

Un exemple concret de cette méthode est la réception d'un chiffre à virgule dans une boite d'édition (TEdit). L'utilisateur peut soit mettre un point, soit mettre une virgule pour définir le séparateur décimal.
Afin d'éviter le déclenchement d'une exception de conversion, il nous faut écrire une méthode simple pour savoir si nous sommes en présence d'un point
ou d'une virgule comme séparateur de décimales.

Exemple :

if pos('.',edit1.text) > 0 then DecimalSeparator := '.'
else
if pos(',',edit1.text) > 0 then DecimalSeparator := ',';

Cette méthode influera directement sur toutes les fonctions de conversion Flottant vers Chaine et inversement, tel Format, FloatToStr, FloatToStrDef, StrToFloat etc....

Après traitement, nous pourrons réassigner à DecimalSeparator, une valeur par défaut (virgule par exemple) et afficher les résultats en conséquence :

DecimalSeparator := ',';
Label1.caption   := Format('mon résultat est : %.2n',[resultat]);

Écriture des spécificateurs d'indice, taille et précision

"Les spécificateurs d'indice, de taille et de précision peuvent être directement spécifiés en utilisant des chaînes contenant des chiffres décimaux (par exemple "%10d") ou indirectement, en utilisant le caractère astérisque (par exemple "%*.*f"). Lorsque vous utilisez l'astérisque, l'argument suivant dans la liste (qui doit être obligatoirement une valeur entière) devient la valeur effectivement utilisée".

Exemple :

format('%.*f',[précision, chiffre]);

Dans cet exemple, le caractère * sera remplacé par le chiffre "précision". Retenez bien cette méthode, car elle vous évitera de déclarer plusieurs constantes de chaine de format et donc permettra encore une fois d'alléger votre code.

Voici un exemple de ce qu'il ne faudrait pas faire :

Function MavaleurToStr(const val: single; const Precision: byte = 0) : string;
const
    Precision0 := '%.0f';
    Precision2 := '%.2f';
    Precision4 := '%.4f';
Begin
    case Precision of
        0 : CDF := Precision0;
        1 : CDF := Precision2;
        2 : CDF := Precision4;
    end;
    result := format(CDF,[val]);
end;

PS: ne rigolez pas, j'ai déjà vu ce genre de code dans des programmes pourtant sérieux. Ce qui démontre bien le manque de connaissance vis à vis de la fonction Format.

Voici maintenant la bonne version de la méthode :

Function MavaleurToStr(const val: single; const Precision: byte = 0) : string;
Begin
    result := format('%.*f',[Precision, val]);
end;

On peut bien sur utiliser "*" pour l'indice de l'argument exemple :

Type
TColor3h = record
Red, Green, Blue : byte;
end;
TColorVl = (cvAll, cvRed, cvGreen, cvBlue);

Function RGBStrHex(const RGB: TColor3h; const color : TColorVl = cvAll) : string;
Begin
    // si on ne précise pas de couleur en particulier
    if color <> cvAll then
        // on renvoie la couleur selon l'indice (ord) de color dans le type TColorvl
        // format récupère ce chiffre comme indice dans le tableau d'arguments
        // et nous renvoie donc la couleur désirée
        result := format('%*:.2x',[ord(color), RGB.Red, RGB.Green, RGB.Blue])
    else
        // sinon, on renvoie tout...
        result := format('%.2x%.2x%.2x',[RGB.Red, RGB.Green, RGB.Blue])
 end;

Dans cette fonction, on raccourci radicalement le code, ce qui évite de faire des cascades de IF ... ELSE ou de CASE ... OF.
La méthode devient alors simple et rapide, tout cela encore, grâce à Format.

Conclusion

Voila, ce tutoriel sur la fonction format est fini.

Vous pouvez en retenir que :

  • Format est la solution la plus pratique pour convertir une valeur en une chaine de caractères.
  • Format est deux fois plus rapide que les méthodes : Chaine = SousChaine + "type"ToStr(valeur).
  • Format permet de réduire considérablement le code des diverses méthodes de conversion vers chaine de caractères.
  • Format rentre complètement dans le cadre de l'"optimisation" des programmes.
A voir également
Ce document intitulé « Formatez vos chaines de caractères grâce à la fonction format » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous