| | |
printf, fprintf, sprintf, snprintf, vprintf,
vfprintf, vsprintf, vsnprintf - Formatage des sorties.
#include <stdio.h>
int printf (const char *format, ...);
int fprintf (FILE *stream, const char *format, ...);
int sprintf (char *str, const char *format, ...);
int snprintf (char *str, size_t size, const char *format, ...);
#include <stdarg.h>
int vprintf (const char *format, va_list ap);
int vfprintf (FILE *stream, const char *format, va_list ap);
int vsprintf (char *str, const char *format, va_list ap);
int vsnprintf (char *str, size_t size, const char *format, va_list ap);
Les fonctions de la famille printf produisent des sorties en
accord avec le format décrit plus bas. Les fonctions printf et vprintf écrivent
leur sortie sur stdout, le flux de sortie standard. fprintf et vfprintf
écrivent sur le flux streamindiqué. sprintf, snprintf, vsprintf et vsnprintf
écrivent leurs sorties dans la chaîne de caractères str.
Les fontions vprintf,
vfprintf, vsprintf, vsnprintf sont équivalentes aux fonctions printf, fprintf,
sprintf, snprintf, respectivement, mais elles emploient un tableau va_list
à la place d'un nombre variable d'arguments. Ces fonctions n'appellent pas
la macro va_end, aussi la valeur de ap est-elle indéfinie après l'appel. Les
applications devraient invoquer va_end(ap) elles-mêmes à la suites de ces
routines.
Ces huit fonctions créent leurs sorties sous le contrôle d'une
chaîne de format qui indique les conversions à apporter aux arguments suivants
(ou accessibles à travers les arguments de taille variable de stdarg(3)
).
Ces fonctions renvoient le nombre de caractères imprimés,
sans compter le caractère nul `\0' final dans les chaînes. snprintf et vsnprintf
n'écrivent pas plus de size octets (y compris le '\0' final), et renvoient
-1 si la sortie a été tronquée à cause de cette limite. Ceci était vrai jusqu'à
la GlibC 2.0.6. Depuis la GlibC 2.1, ces fonctions suivent le standard C99
et renvoient le nombre de caractères (sans compter le '\0' final) qui auraient
été écrits si la chaîne à remplir avait été assez longue.
Le
format de conversion est indiqué par une chaîne de caractères, commençant
et se terminant dans son état de décalage initial. La chaîne de format est
composée d'indicateurs : les caractères ordinaires (différents de %), qui
sont copiés sans modification sur la sortie, et les spécifications de conversion,
qui sont mises en correspondances avec les arguments suivants. Les spécifications
de conversion sont introduites par le caractère %, et se terminent par
un indicateur de conversion. Entre eux peuvent se trouver (dans l'ordre),
zéro ou plusieurs attributs, une valeur optionnelle de largeur minimal
de champ, une valeur optionnelle de précision, et un éventuel modificateur
de longueur.
Les arguments doivent correspondre correctement (après les
promotions de types) avec les indicateurs de conversion. Par défaut les
arguments sont pris dans l'ordre indiqué, où chaque '*' et chaque indicateur
de conversion réclament un nouvel argument (et où l'insuffisance en arguments
est une erreur. On peut aussi préciser explicitement quel argument prendre,
en écrivant, à chaque conversion, `%m$' au lieu de `%', et `*m$' au lieu de `*'.
L'entier décimal m indique la position dans la liste d'arguments, l'indexation
commençant à 1. Ainsi,
printf ("%*d", width, num);
et
printf ("%2$*1$d", width, num);
sont équivalents. La seconde notation permet de répéter plusieurs fois le
même argument. Le standard C99 n'autorise pas le style utilisant `$'.
Pour
certaines conversions numériques, un caractère de séparation décimale (le
point par défaut) est utilisé, ainsi qu'un caractère de regroupement par
milliers '. Les véritables caractères dépendent de la localisation LC_NUMERIC.
La localisation POSIX utilise `.' comme séparateur décimal, et n'a pas de caractère
de regroupement. Ainsi,
printf ("%'.2f", 1234567.89);
s'affichera comme 1234567.89 dans la localisation POSIX, 1234567,89 en localisation
fr_FR, et 1.234.567,89 en localisation da_DK.
Le caractère
% peut être éventuellement suivi par un ou plusieurs attributs suivants
:
- #
- indique que la valeur doit être convertie en une autre forme. Pour la
conversion o le premier caractère de la chaîne de sortie vaudra zéro (en
ajoutant un préfixe 0 si ce n'est pas déjà un zéro). Pour les conversions
x et X une valeur non nulle reçoit le préfixe `0x' (ou `0X' pour l'indicateur
X). Pour les conversions a, A, e, E, f, g, et G le résultat contiendra toujours
un point décimal même si aucun chifre ne le suit (normalement, un point
décimal n'est présent avec ces conversions que si des décimales le suivent).
Pour les conversions g et G les zéros en tête ne sont pas éliminés, contrairement
au comportement habituel. Pour les autres conversions, cet attribut n'a pas
d'effet.
- indique le remplissage avec des zéros. Pour les conversions d, i,
o, u, x, X, a, A, e, E, f, F, g, et G, la valeur est complétée à gauche
avec des zéros plutot qu'avec des espaces. Si les attributs 0 et - apparaissent
ensemble, l'attribut 0 est ignoré. Si une précision est fournie avec une
conversion numérique (d, i, o, u, x, et X), l'attribut 0 est ignoré. Pour
les autres conversions, le comportement est indéfini.
- -
- indique que la valeur
doit être justifiée sur la limite gauche du champ (par défaut elle l'est
à droite). Sauf pour la conversion n, les valeurs sont complétées à droite
par des espaces, plutôt qu'a gauche par des zéros ou des blancs. Un attribut
- surcharge un attribut 0 si les deux sont fournis.
- ' '
- (un espace) indique
qu'un espace doit être laissé avant un nombre positif (ou une chaîne vide)
produit par une conversion signée
- +
- indique que le signe doit toujours
être imprimé avant un nombre produit par une conversion signée. Un attribut
+ surcharge un attribut 'espace' si les deux sont fournis.
Les cinq caractères
d'attributs ci-dessus sont définis dans le standard C, les spécifications
SUSv2 en ajoute un !
- '
- Pour les conversions décimales (i, d, u, f, g, G)
indique que les chiffres d'un argument numérique doivent être groupés par
millier en fonction de la localisation. Remarquez que de nombreuses versions
de gcc n'accepte pas cet attribut et déclencheront un avertissement (warning).
Un nombre optionnel ne commençant pas par un zéro, peut
indiquer une largeur minimale de champ. Si la valeur convertie occupe moins
de caractères que cette largeur, elle sera complétée par des espaces à
gauche (ou à droite si l'attribut d'alignement à gauche a été fourni). À la
place de la chaîne représentant le nombre décimal, on peut écrire `*' ou
`*m$' (m étant entier) pour indiquer que la largeur du champ est fournie
dans l'argument suivant, ou dans le m-ième argument, respectivement. L'argument
fournissant la largeur doit être de type int. Une largeur négative est considéré
comme l'attribut `-' vu plus haut suivi d'une largeur positive. En aucun cas
une largeur trop petite ne provoque la troncature du champ. Si le résultat
de la conversion est plus grand que la largeur indiquée, le champ est élargi
pour contenir le résultat.
Une précision eventuelle, sous la forme
d'un point (`.') suivi par un nombre. À la place de la chaîne représentant
le nombre décimal, on peut écrire `*' ou `*m$' (m étant entier) pour indiquer
que la précision est fournie dans l'argument suivant, ou dans le m-ième argument,
respectivement. L'argument fournissant la précision doit être de type int.
Si la précision ne contient que le caractère `.', ou une valeur négative,
elle est considérée comme nulle. Cette précision indique un nombre minimum
de chiffres à faire apparaître lors des conversions d, i, o, u, x, et X,
le nombre de décimales à faire apparaître pour les conversions a, A, e,
E, f, et F, le nombre maximum de chiffres significatifs pour g et G, et
le nombre maximum de caractères à imprimer depuis une chaîne pour les conversions
S et s.
Ici, une conversion entière correspond
à d, i, o, u, x, ou X.
- hh
- La conversion entière suivante correspond à un
signed char ou unsigned char, ou la conversion n suivante correspond à
un argument pointeur sur un signed char.
- h
- La conversion entière suivante
correspond à un short int ou unsigned short int, ou la conversion n suivante
correspond à un argument pointeur sur un short int.
- l
- (elle) La conversion
entière suivante correspond à un long int ou unsigned long int, ou la conversion
n suivante correspond à un pointeur sur un long int, ou la conversion c
suivante correspond à un argument wint_t, ou encore la conversion s suivante
correspond à un pointeur sur un wchar_t.
- ll
- (elle-elle) La conversion entière
suivante correspond à un long long int, ou unsigned long long int, ou la
conversion n suivante correspond à un pointeur sur un long long int.
- L
- La conversion a, A, e, E, f, F, g, ou G suivante correspond à un argument
long double. (C99 autorise %LF mais pas SUSv2).
- q
- (`quad' BSD 4.4 et Linux sous
libc5 seulement, ne pas utiliser) Il s'agit d'un synonyme pour ll.
- j
- La conversion
entière suivante correspond à un argument intmax_t ou uintmax_t.
- z
- La conversion
entière suivante correspond à un argument size_t ou ssize_t. (La bibliothèque
libc5 de Linux proposait l'argument Z pour cela, ne pas utiliser).
- t
- La conversion
entière suivante correspond à un argument ptrdiff_t.
Les spécifications
SUSv2 ne mentionnent que les modificateurs de longueur h (dans hd, hi,
ho, hx, hX, hn),
l (dans ld, li, lo, lx, lX, ln, lc, ls) et L (dans Le,
LE, Lf, Lg, LG).
Un caractère indique le type de
conversion à apporter. Les indicateurs de conversion, et leurs significations
sont :
- d, i
- L'argument int est convertie en un chiffre décimal signé. La
précision, si elle est mentionné, correspond au nombre minimal de chiffres
qui doivent apparaître. Si la conversion fournit moins de chiffres, le résultat
est rempli à gauche avec des zéros. Par défaut la précision vaut 1. Lorsque
0 est converti avec une précision valant 0, la sortie est vide.
- o, u, x,
X
- L'argument unsigned int est converti en un chiffre octal non-signé (o),
un chiffre décimal non-signé (u), un chiffre héxadécimal non-signé (x et
X). Les lettres abcdef sont utilisées pour les conversions avec x, les lettres
ABCDEF sont utilisées pour les conversions avec X. La précision, si elle
est indiquée, donne un nombre minimal de chiffres à faire apparaître. Si
la valeur convertie nécessite moins de chiffres, elle est complétée à gauche
avec des zéros. La précision par défaut vaut 1. Lorsque 0 est converti avec
une précision valant 0, la sortie est vide.
- e ", " E
- L'argument réel, de
type double, est arrondi et présenté avec la notation scientifique [-]c.ccce\*(Pmcc
dans lequel se trouve un chiffre avant le point, puis un nombre de décimales
égal à la précision demandée. Si la précision n'est pas indiquée, l'affichage
contiendra 6 décimales. Si la précision vaut zéro, il n'y a pas de point
décimal. Une conversion E utilise la lettre E (plutôt que e) pour introduire
l'exposant. Celui-ci contient toujours au moins deux chiffres. Si la valeur
affichée est nulle, son exposant est 00.
- f, F
- L'argument réel, de type double,
est arrondi, et présenté avec la notation classique [-]ccc.ccc, où le nombre
de décimales est égal à la précision réclamée. Si la précision n'est pas
indiquée, l'affichage se fera avec 6 décimales. Si la précision vaut zéro,
aucun point n'est affiché. Lorsque le point est affiché, il y a toujours
au moins un chiffre devant.
SUSv2 ne mentionne pas F et dit qu'il existe
une chaîne de caractères représentant l'infini ou NaN. Le standard C99 précise
`[-]inf' ou `[-]infinity' pour les infinis, et une chaîne commençant par `nan'
pour NaN dans le cas d'une conversion f, et les chaînes `[-]INF' `[-]INFINITY'
`NAN*' pour une conversion F.
- g, G
- L'argument réel, de type double, est converti
en style f ou e (ou E pour la conversion G) La précision indique le nombre
de décimales significatives. Si la précision est absente, une valeur par
défaut de 6 est utilisée. Si la précision vaut 0, elle est considérée comme
valant 1. La notation scientifique e est utilisée si l'exposant est inférieur
à -4 ou supérieur ou égal à la précision démandée. Les zéros en fin de partie
décimale sont supprimés. Un point decimal n'est affiché que s'il est suivi
d'au moins un chiffre.
- a, A
- (C99 mais pas SUSv2). Pour la conversion a, l'argument
de type double est transformé en notation hexadécimale (avec les lettres
abcdef) dans le style [-]0xh.hhhhp\*(Pmd; Pour la conversion A, le préfixe
0X, les lettres ABCDEF et le séparateur d'exposant P sont utilisés. Il y
a un chiffre hexadécimal avant la virgule, et le nombre de chiffres ensuite
est égal à la précision. La précision par défaut suffit pour une représentation
exacte de la valeur, si une représentation exacte est possible en base
2. Sinon elle est suffisament grande pour distinguer les valeurs de type
double. Le chiffre avant le point décimal n'est pas spécifié pour les nombres
non-normalisés, et il non-nul pour les nombres normalisés.
- c
- S'il n'y a pas
de modificateur l, l'argument entier, de type int, est converti en un unsigned
char, et le caractère correspondant est affiché. Si un modificateur l est
présent, l'argument de type wint_t (caractère large) est converti en séquence
multi-octet par un appel à wctomb, avec un état de conversion débutant dans
l'état initial. La chaîne multi-octet résultante est écrite.
- s
- S'il n'y a pas
de modificateur l, l'argument de type const char * est supposé être un pointeur
sur un tableau de caractères (pointeur sur une chaîne). Les caractères du
tableau sont écrits jusqu'au caractère NUL final, non compris. Si une précision
est indiquée, seul ce nombre de caractères sont écrits. Si une précision
est fournie, il n'y a pas besoin de caractère nul. Si la précision n'est pas
donnée, ou si elle est supérieure à la longueur de la chaîne, le caractère
NUL final est nécessaire.
Si un modificateur l est présent, l'argument
de type const wchar_t * est supposé être un pointeur sur un tableau de
caractères larges. Les caractères larges du tableau sont convertis en une
séquence de caractères multi-octets (chacun par un appel de wctomb, avec
un état de conversion dans l'état initial avant le premier caractère large),
ceci jusqu'au caractère large nul final compris. Les caractères multi-octets
résultants sont écris jusqu'à l'octet nul final (non compris). Si une précision
est fournie, il n'y a pas plus d'octets écrits que la précision indiquée,
mais aucun caractère multi-octet n'est écrit partiellement. Remarquez que
la précision concerne le nombre d'octets écrits, et non pas le nombre de
caractères larges ou de positions d'écrans. La chaîne doit contenir un caractère
large nul final, sauf si une précision est indiquée, suffisament petite
pour que le nombre d'octets écrits la remplisse avant la fin de la chaîne.
- C
- (pas dans C99) Synonyme de lc. Ne pas utiliser.
- S
- (pas dans C99) Synonyme
de ls. Ne pas utiliser.
- p
- L'argument pointeur, du type void * est affiché
en héxadécimal, comme avec %#x ou %#lx.
- n
- Le nombre de caractères déjà écrits
est stocké dans l'entier indiqué par l'argument pointeur de type int *. Aucun
argument n'est converti.
- %
- Un caractère `%' est écrit. Il n'y a pas de conversion.
L'indicateur complet est `%%'.
Pour afficher Pi avec cinq décimales :
#include <math.h>
#include <stdio.h>
fprintf (stdout, "pi = %.5f\n", 4 * atan (1.0));
Pour afficher une date et une heure sous la forme `Dimanche 10 Novembre,
23:15', ou jour_semaine et mois sont des pointeurs sur des chaînes :
#include <stdio.h>
fprintf (stdout, "%s %d %s, %.2d:%.2d\n",
jour_semaine, jour, mois, heure, minute);
Pour allouer 128 octets de chaînes de caractères, et écrire dedans :
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *
newfmt (const char *fmt, ...)
{
char *p;
va_list ap;
if ((p = malloc(128)) == NULL)
return (NULL);
va_start(ap, fmt);
(void) vsnprintf(p, 128, fmt, ap);
va_end(ap);
return (p);
}
printf(1)
, wcrtomb(3)
, wprintf(3)
, scanf(3)
, locale(5)
.
Les
fonctions fprintf, printf, sprintf, vprintf, vfprintf, et vsprintf sont
conformes à ANSI X3.159-1989 (``ANSI C'') et ISO/IEC 9899:1999 (``ISO C99''). Les
fonctions snprintf, et vsnprintf sont conformes à ISO/IEC 9899:1999.
En
ce qui concerne la valeur de retour de snprintf, SUSv2 et C99 sont en contradiction
: lorsque snprintf est appelée avec un argument size=0 lors SUSv2 précise
une valeur de retour indéterminée, autre que 1, alors que C99 autorise
str à être NULL dans ce cas, et réclame en valeur de retour (comme toujours)
le nombre de caractères qui auraient été écrits si la chaîne de sortie
avait été assez grande.
La bibliothèque libc4 de Linux connaissait les 5
attributs standards du C. Elle connaissait les modificateurs de longueur
h, l, L et les conversions cdeEfFgGinopsuxX, où F était synonyme de f. De
plus elle acceptait D, O, U comme synonymes de ld, lo et lu. (Ce qui causa
de sérieux bogues par la suite lorsque le support de %D disparut). Il n'y
avait pas de séparateur décimal dépendant de la localisation, pas de séparateur
des milliers, par de NaN ou d'infinis, et pas de %m$ ni *m$.
La bibliothèque
libc5 de Linux connaissait tous les 6 attributs, la localisation, %m$ et
*m$. Elle connaissait les modificateurs de longueur h, l, L, Z, q, mais
acceptait L et q pour les "long double" et les "long long integer" (ce
qui est un bogue). Elle ne reconnaissait plus FDOU, mais ajoutait le caractère
de conversion m, qui affiche strerror (errno).
La bibliothèque GlibC 2.0
ajouta les caractères de conversion C et S.
La bibliothèque GlibC 2.1 ajouta
les modificateurs de longueur hh, t, z, et les caractères de conversion
a, A.
Unix V7 définissait les trois routines printf, fprintf,
sprintf, et l'attribut -, la largeur ou la précision *, le modificateur de
longueur l, et les conversions doxfegcsu, ainsi que D, O, U, X comme synonymes
de ld, lo, lu, lx. Ceci est vrai pour BSD 2.9.1, mais BSD 2.10 avait les attributs
#, + et <espace> mais ne mentionnait plus D, O, U, X. BSD 2.11 avait vprintf,
vfprintf, vsprintf, et signalait le problème de D, O, U, X. BSD 4.3 Reno
avait l'attribut 0, les modificateurs de longueur h et L, ainsi que les
conversions n, p, E, G, X (avec sa signification actuelle), et dénonçait
D, O, U. BSD 4.4 introduisit les fonctions snprintf et vsnprintf, et le
modificateur de longueur q. FreeBSD avait aussi les fonctions asprintf et
vasprintf, qui allouaient un buffer assez grand pour sprintf.
Comme
sprintf et vsprintf ne font pas de suppositions sur la longueur des chaînes,
le programme appelant doit s'assurer de ne pas déborder l'espace d'adressage.
C'est souvent difficile. Notez que la longueur des chaînes peut varier avec
la localisation et être difficilement prévisible. Il faut alors utiliser
snprintf ou vsnprintf à la place (ou encore asprintf et vasprintf).
La libc4.[45]
de Linux n'avait pas snprintf, mais proposait une bibliothèque libbsd qui
contenait un snprintf équivalent à sprintf, c'est à dire qui ignorait l'argument
size. Ainsi, l'utilisation de snprintf avec les anciennes libc4 pouvait conduire
à de sérieux problèmes de sécurité.
Certaines conversions de nombres réels,
avec les anciennes libc4, pouvaient provoquer des fuites de mémoire.
Christophe
Blaess, 1997.
Table des matières
© 1996-2000 Adaptation française "Christophe Blaess"
| |