printf(3) vsnprintf

書式

#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);

glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):

snprintf(), vsnprintf():

_BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;
または cc -std=c99

説明

printf() 関数グループは、以下で述べるように、 format に従って出力を生成するものである。 printf() と vprintf() は出力を stdout (標準出力ストリーム) に書き出す。 fprintf() と vfprintf() は出力を指定された出力 stream に書き出す。 sprintf(), snprintf(), vsprintf(), vsnprintf() は出力を文字列 str に書き込む。

snprintf() と vsnprintf() は最大で size バイトを str に書き込む (size には文字列を終端するヌルバイト ('\0') もを含まれる)。

vprintf(), vfprintf(), vsprintf(), vsnprintf() の各関数はそれぞれ printf(), fprintf(), sprintf(), snprintf(), の各関数と等価であり、可変数引き数の代わりに va_list を引き数として呼び出される点だけが異なる。 これらの関数では va_end マクロは呼び出されない。 これらの関数は va_arg を呼び出すので、呼び出し後の ap の値は未定義である。 stdarg(3) を参照のこと。

これらの 8 つの関数は format 文字列の制御に従って出力を書き出す。 format 文字列は、これに続く引き数 (または stdarg(3) の可変長引き数機構を使ってアクセスできる引き数) をどのように変換して出力するかを指定する。

C99 と POSIX.1-2001 では、 sprintf(), snprintf(), vsprintf(), vsnprintf() の呼び出しで、範囲が重複するオブジェクト間でコピーが発生する場合の 結果は不定であると規定されている (例えば、出力先の文字列と入力された 引き数の一つが同じバッファを参照している場合などである)。 「注意」の節を参照。

Return Values

成功時には、上記の関数は書き込まれた文字数を返す (文字列の最後を示すために使用するヌルバイトは数に含まれない)。

snprintf() と vsnprintf() は、 size バイトを越える文字数を書き込まない (size には文字列を終端するヌルバイト ('\0') も含まれる)。 この制限によって出力が切り詰められた場合には、 もし十分なスペースがあれば書き込まれたであろう文字の個数 (文字列を終端するヌルバイトを除く) を返す。 従って、返り値が size 以上だった場合、出力が切り詰められたことを意味する (後述の注意も参照のこと)。

エラーが発生した場合は、負の数を返す。

フォーマット文字列のフォーマット

フォーマット文字列は文字の列で、 (もしあるなら) 初期シフト状態で始まり、初期シフト状態で終わる。 フォーマット用の文字列は 0 個以上の命令 (directives) によって構成される。 命令には、通常文字と変換指定 (conversion specifications) がある。 通常文字は % 以外の文字で、出力ストリームにそのままコピーされる。 変換指定は、それぞれが 0 個以上の引き数を取る。 各変換指定は文字 % で始まり、 変換指定子 (conversion specifier) で終わる。 % と変換指定子の間には、0 個以上の フラグ 、 最小 フィールド幅 、 精度 、 長さ修飾子 を (この順序で) 置くことができる。

引き数は (型の格上げの後は) 変換指定子が表す型と正確に対応しなければならない。 デフォルトでは、'*' や変換指定子が出てくる毎に次の引き数を要求され、 引き数は指定された順序で使用されていく (指定された引き数の個数が不十分ならエラーとなる)。 また、引き数が必要な箇所で '%' の代わりに "%m$"、 '*'の代わりに "*m$" と書くことで、 明示的にどの引き数を使用するかを指定することもできる。 ここで 10進の整数 m は希望の引き数の引き数リストでの位置を示す (最初の引き数の番号が 1 である)。 従って、

printf("%*d", width, num);
printf("%2$*1$d", width, num);
は等価である。 二番目の書き方では同じ引き数を繰り返し参照することができる。 C99 標準には、 Single UNIX Specification 由来の '$' を使った書き方は含まれていない。 '$' を使ったスタイルを使うと、引き数を取る変換及び幅と精度の引き数を 全てこのスタイルで指定しなければならないが、 引き数を消費しない "%%" フォーマットと混ざっているかもしれない。 '$' で指定される引き数の番号に空きがあってはならない。 例えば、もし引き数 1 と 3 が指定されると、引き数 2 もフォーマット文字列のどこかで 指定されなければならない。

数値変換には小数点や 1000 単位の区切り文字を使うものもある。 実際にどの文字を使うかはロケールの LC_NUMERIC による。 POSIX ロケールでは小数点に '.' を用い、 区切り文字は使わない。 従って、

printf("%'.2f", 1234567.89);
は、 POSIX ロケールでは "1234567.89" 、 nl_NL ロケールでは "1234567,89"、 da_DK ロケールでは "1.234.567,89" となる。

フラグ文字

% 文字の後ろには 0 個以上のフラグ文字が続く。
#
値は「別の形式」に変換される。 o 変換の場合、(先頭文字が 0 になっていない場合に先頭に 0 を追加することで) 出力文字列の最初の文字を 0 にする。 xX 変換の場合、数値が 0 でないときには文字列 "0x" (X 変換の場合には "0X") が前に付与される。 a, A, e, E, f, F, g, G 変換では、 小数点に続く数字がなくても、 出力には常に小数点が含まれる (通常は、小数点の後に数字が続く場合にのみ、 小数点が表示される)。 gG 変換の場合、他の変換とは異なり、末尾のゼロが変換結果から削除されない。 その他の変換では、結果は未定義である。
0
値をゼロで埋める。 d, i, o, u, x, X, a, A, e, E, f, F, g, G 変換では、変換した値の左側を空白文字の代わりにゼロで埋める。 0- が両方とも指定された場合は、 0 フラグは無視される。 精度が数値変換 (d, i, o, u, x, X) と同時に指定された場合には、 0 フラグは無視される。 その他の変換では、動作は未定義である。
-
変換値をフィールド境界で左揃えにする (デフォルトは右揃えである)。 n 変換以外では、変換された値は 左側ではなく右側を空白文字やゼロで埋められる。 -0 の両方が指定された場合には、 - が優先される。
' '
(1個の半角スペース) 符号付き変換で生成された正の数字の前に空白 (または空文字列) が置かれる。
+
符号付き変換によって出力される数字の前に、常に符号 (+ か -) が置かれる。 デフォルトでは、符号は負の数字の場合のみ付与される。 + と半角スペースの 両方が使われている場合には、 + が優先される。

上記の 5 つのフラグは C 標準で定義されている。 SUSv2 では、さらにもう一つフラグ文字が規定されている。

'
10進数変換 (i, d, u, f, F, g, G) において、ロケール情報に指定があれば 1000 単位の区切り文字を出力する。 gcc(1) の多くのバージョンは、このオプションを解釈することができず、 警告を出力することに注意せよ。 %'F は SUSv2 には含まれていない。

glibc 2.2 では、さらに一つフラグ文字が追加されている。

I
10進整数変換 (i, d, u) において、ロケールの代替出力数字があれば、それを用いて出力する。 例えば、 glibc 2.2.3 以降では、ペルシア ("fa_IR") ロケールで アラビア数字 (Arabic-Indic digits) を出力できる。

フィールド幅

最小のフィールド幅を指定する 10進数の数値文字列 (文字列の最初の文字は ゼロ以外)。本項目はオプションである。 変換された値の文字数がフィールド長よりも少ない場合、 フィールドの左側をスペースで埋める (左揃えのフラグがある場合は右側を埋める)。 10進数の文字列の代わりに "*" や "*m$" (m は 10進整数) を書くこともできる。 "*" と "*m$" はそれぞれ、次の引き数と m 番目の引き数をフィールド幅として 使うことを指定する (これらの引き数は int 型でなければならない)。 フィールド幅に負の数が指定された場合は、 '-' フラグと正の数のフィールド幅として扱われる。 フィールド幅が小さかったり指定がなかったりしても、フィールドが切り詰められる ことはない。もし変換結果がフィールド幅よりも広かった場合、 フィールドは変換結果が入る幅に広げられる。

精度

オプションである精度は、ピリオド ('.') とそれに続く10進数という 形式で指定する (10進数はオプション) 。 10進数の文字列の代わりに "*" や "*m$" (m は 10 進整数)を書くこともできる。 "*" と "*m$" はそれぞれ、次の引き数と m 番目の引き数を精度として 使うことを指定する (これらの引き数は int 型でなければならない)。 精度として '.' だけが指定された場合、 精度はゼロとみなされる。 精度が負の数だった場合、 精度は指定されなかったものとみなされる。 d, i, o, u, x, X 変換では、表示される最小の桁数を指定する。 a, A, e, E, f, F 変換では、小数点以下に表示される数字の桁数を指定する。 gG 変換では、有効数字の最大桁数を指定する。 sS 変換では、文字列から出力される最大文字数を指定する。

長さ修飾子

「整数変換」とは、 d, i, o, u, x, X 変換のことである。
hh
整数変換に対応する引き数が signed charunsigned char で、 n 変換に対応する引き数が signed char へのポインタであることを示す。
h
整数変換に対応する引き数が short intunsigned short int で、 n 変換に対応する引き数が short int へのポインタであることを示す。
l
各変換に対応する引き数が、 整数変換では long intunsigned long intn 変換では long long int へのポインタ、 c 変換では wint_ts 変換では wchar_t へのポインタであることを示す。
ll (エルエル)
整数変換に対応する引き数が long long intunsigned long long int で、 n 変換に対応する引き数が long int へのポインタであることを示す。
L
a, A, e, E, f, F, g, G 変換に対応する引き数が long double であることを示す。 (C99 では %LF を使うことを認めているが、SUSv2 では認められていない。)
q
("quad"。 4.4BSD と Linux libc5 のみ有効。使ってはならない。) ll と同じ意味である。
j
整数変換に対応する引き数が intmax_tuintmax_t であることを示す。
z
整数変換に対応する引き数が size_tssize_t であることを示す。 (Linux libc5 では、これを指定するのに Z を用いる。使ってはならない。)
t
整数変換に対応する引き数が ptrdiff_t であることを示す。

SUSv2 で長さ修飾子として使用できるのは、 h (hd, hi, ho, hx, hX, hn), l (ld, li, lo, lx, lX, ln, lc, ls), L (Le, LE, Lf, Lg, LG) だけである。

変換指定子

適用される変換の型を指定する文字。 変換指定子とその意味は以下の通りである。
d, i
int 引き数を符号付き 10 進表記に変換する。 精度指定があれば、精度で指定した桁数は必ず出力される。変換後の値が 指定された桁数に足りない場合は、左側が 0 で埋められる。 デフォルトの精度は 1 である。 0 を表示しようとした時に、明示的に精度として 0 が指定されていると、 出力は空文字列となる。
o, u, x, X
unsigned int 引き数を、 符号なし8進数 (o), 符号なし10進数 (u), 符号なし16進数 (xX) に変換する。 x 変換では abcdef が使用され、 X 変換では ABCDEF が使用される。 精度指定があれば、精度で指定した桁数は必ず出力される。変換後の値が 指定された桁数に足りない場合は、左側が 0 で埋められる。
e, E
double 引き数を丸めて [-]d.ddde±dd の形に変換する。 小数点の前には一桁の数字があり、小数点以下の桁数は精度で指定された桁数 になる。精度は指定されなかった場合 6 とみなされる。 精度が 0 の場合には、 小数点以下は表示されない。E 変換では、指数を表現するときに (e で はなく) E が使われる。指数部分は少なくとも 2桁表示される。つまり、 指数の値が 0 の場合には、00 と表示される。
f, F
double 引き数を丸めて [-]ddd.ddd の形の10進表現に変換する。 小数点の後の桁数は、精度で指定された値となる。 精度が指定されていない場合には 6 として扱われる。 精度として明示的に 0 が指定されたときには、小数点以下は表示されない。 小数点を表示する際には、小数点の前に少なくとも一桁は数字が表示される。

(SUSv2 では、F は規定されておらず、無限や NaN に関する文字列表現を 行ってもよいことになっている。
 C99 標準では、f 変換では、無限は "[-]inf" か "[-]infinity" と表示し、 NaN は文字列の先頭に `nan' をつけて表示するように規定されている。 F 変換の場合は "[-]INF", "[-]INFINITY", "NAN*" と表示される。)

g, G
double 引き数を fe (G 変換の場合は FE) の形式に変換する。 精度は表示する桁数を指定する。 精度が指定されない場合は、6桁とみなされる。 精度が 0 の場合は、1桁とみなされる。 変換される値の指数が、 -4 より小さいか、精度以上の場合に、 e 形式が使用される。 変換された結果の小数部分の末尾の 0 は削除される。小数点が表示されるのは、 小数点以下に数字が少なくとも一つある場合にだけである。
a, A
(C99 にはあるが SUSv2 にはない) a 変換では、 double 引き数を (abcdef の文字を使って) [-]0xh.hhhhp± 形式の 16 進表記に変換する。 A 変換では、前置文字列 0X, 文字 ABCDEF, 指数文字 P を用いる。 小数点の前には 1 桁の 16 進数が置かれ、小数点の後ろの桁数は 精度で指定 された値となる。デフォルトの精度は、その値が 2 進数で正確に表現できる 場合には、その値を正確に表現できる桁数となる。それ以外の場合は、 double 型の値を区別するのに十分な大きさとなる。 小数点の前の数字は、 正規化されていない数の場合はいくつになるか分からない。 正規化された数の 場合は、 0 以外の値になるが、いくつになるかは分からない。
c
l 修飾子がなければ、 int 引き数を unsigned char に変換して、その結果に対応する文字を出力する。 l 修飾子があれば、 wint_t (ワイド文字) 引き数を、 wcrtomb(3) 関数を初期シフト状態で呼び出してマルチバイト文字列に変換し、 変換されたマルチバイト文字列を出力する。
s
l 修飾子がない場合、 引き数は const char * 型で文字型の配列へのポインタ (文字列へのポインタ) であることが 期待されている。配列中の文字は、終端の ヌルバイト ('\0') が出てくるまで出力される (終端文字は出力されない)。 精度が指定されていると、指定された字数以上は出力されない。 精度が指定された場合には、終端バイトが存在する必要はない。 精度が指定されていなかったり、精度の値が配列の大きさより大きい場合には、 配列は終端のヌルバイトを含んでいなければならない。

l 修飾子が指定されている場合、 引き数は const wchar_t * 型でワイド文字の配列へのポインタであることが期待されている。 配列中のワイド文字は (1文字毎に wcrtomb(3) を呼び出して) マルチバイト文字に変換される (最初のワイド文字の変換の前に wcrtomb() のシフト状態を初期状態に戻してから変換は行われる)。 マルチバイト文字への変換は、文字列を終端するヌルワイド文字が 出てくるまで行われ、終端ヌルワイド文字も含めて変換される。 結果のマルチバイト文字列は、終端のヌルバイトが出てくるまで 出力される (終端のヌルバイトは出力されない)。 精度が指定された場合、指定されたバイト数以上には出力されない。 但し、マルチバイト文字の一部分だけが出力されることはない。 精度は「バイト」数を指定するものであり、「ワイド文字」数や 「画面での位置」を指定するものではないことに注意。 精度が指定されていて、さらに出力が配列の末尾に達する前に出力バイト数が 精度の値を超える場合だけは、配列はヌルワイド文字で終端されていなくてもよい。 それ以外の場合は、必ず配列はヌルワイド文字で終端されていなければならない。

C
(C99 にはないが SUSv2 にはある) lc と同じ。使ってはならない。
S
(C99 にはないが SUSv2 にはある) ls と同じ。使ってはならない。
p
void * ポインタ引き数を (%#x%#lx のような) 16 進数で出力する。
n
これまでに出力された文字数を int * (または類似の型) のポインタ引き数が指す整数に保存する。 引き数の変換は行われない。
m
(glibc での拡張) strerror(errno) の出力を表示する。引き数は必要ない。
%
'%' 文字を出力する。変換される引き数は無い。 変換指定全体を書くと "%%" となる。

準拠

fprintf(), printf(), sprintf(), vprintf(), vfprintf(), vsprintf() 関数は、C89 と C99 に準拠している。 snprintf() と vsnprintf() は C99 に準拠している。

snprintf() の返り値を見ると、 SUSv2 と C99 標準は互いに矛盾している。 SUSv2 では、 snprintf() が size=0 で呼び出された場合、 1 未満の値を何か返り値とするように規定している。 一方 C99 では、このような場合 str を NULL とし、返り値として (通常通り) 出力バッファが十分な大きさが あった場合に出力されるであろう文字数を返す。

Linux libc4 では、 5 つの C 標準のフラグ、 長さ修飾子 h, l, L、変換 c, d, e, E, f, F, g, G, i, n, o, p, s, u, x, X が使える。 但し Ff と同義である。 また、 D, O, and Uld, lo, and lu と同じものとして使える (これはまずい仕様で、 後に %D の対応が打ち切られた時に深刻なバグを 引き起こした)。ロケール依存の小数点、1000 区切り、 NaN と無限、 "%m$" と "*m$" は使えない。

Linux libc5 では、 5 つの C 標準のフラグと ' フラグ、ロケール、 "%m$" と "*m$" が使える。 また、長さ修飾子 h, l, L, Z, iand q が使えるが、 Lq は両方とも long doublelong long int に対応している (これはバグである)。 現在では変換 F, D, O, U は認識されないが、変換文字 m が追加された。これは strerror(errno) を出力するものである。

glibc 2.0 では、変換文字 CS が追加された。

glibc 2.1 では、長さ修飾子 hh, j, t, z と変換文字 a, A が追加された。

glibc 2.2 では、 C99 で規定された意味での変換文字 F と フラグ文字 I が追加された。

注意

テキストを buf に追加するのに、軽率にも次のようなコードを使っているプログラムがある。


    sprintf(buf, "%s some further text", buf);

しかしながら、標準規格では、 sprintf(), snprintf(), vsprintf(), vsnprintf() の呼び出しにおいて、コピー元とコピー先のバッファが重なっていた場合の 結果は不定である、と明記されている。 使用する gcc(1) のバージョンや指定したコンパイラのオプション次第では、 上記のような呼び出しで、期待した結果が得られ「ない」ことがある。

glibc の snprintf() と vsnprintf() の実装は、バージョン 2.1 以降は C99 標準に準拠しており、 上記の通りの動作をする。 glibc 2.0.6 までは、出力が切り詰められた場合は -1 を返す。

バグ

sprintf() と vsprintf() は勝手に十分に長い文字列領域があると仮定するので、呼び出し側は 実際の領域からあふれないように注意しなければならない。 しかし、これを保証することが不可能な場合が多い。 生成される文字列の長さはロケール依存であり、予測が難しいことに注意。 代わりに snprintf() と vsnprintf() (または asprintf(3) と vasprintf(3)) を使うこと。

Linux libc4.[45] には snprintf() はないが、 libbsd が提供されており、 その中には sprintf() と等価な (つまり size 引き数を無視する) snprintf() がある。 したがって、初期の libc4 で snprintf() を使うと、深刻なセキュリティ問題を引き起こすことがある。

printf(foo); のようなコードはしばしばバグを引き起こす。 なぜなら foo に % 文字が含まれてるかもしれないからである。 foo が信頼できないユーザー入力から作られている場合には、 その中に %n が含まれていることがあり、 printf() 呼び出し時にメモリへの書き込みが起こり、 セキュリティーホールを作ることになるかもしれない。

Pi を 5 桁で出力する。
#include <math.h>
#include <stdio.h>
fprintf(stdout, "pi = %.5f\n", 4 * atan(1.0));

日付と時間を "Sunday, July 3, 10:02" の形式で出力する。 (weekdaymonth は文字列へのポインタである)

#include <stdio.h>
fprintf(stdout, "%s, %s %d, %.2d:%.2d\n",
        weekday, month, day, hour, min);

日 - 月 - 年 の順序で表示を行う国も多い。 従って、国際版では書式で指定された順番で 引き数を表示できなければならない。

#include <stdio.h>
fprintf(stdout, format,
        weekday, month, day, hour, min);
format はロケールに依存しており、引き数の順番を変えることもできる。 format
"%1$s, %3$d. %2$s, %4$d:%5$.2d\n"
であれば、 "Sonntag, 3. Juli, 10:02" という結果になる。

十分に大きな文字列領域を確保して、そこにメッセージを格納するには (glibc 2.0 と glibc 2.1 の両方で正しく動作するコード):

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *
make_message(const char *fmt, ...)
{
    int n;
    int size = 100;     /* Guess we need no more than 100 bytes */
    char *p, *np;
    va_list ap;
    p = malloc(size);
    if (p == NULL)
        return NULL;
    while (1) {
        /* Try to print in the allocated space */
        va_start(ap, fmt);
        n = vsnprintf(p, size, fmt, ap);
        va_end(ap);
        /* Check error code */
        if (n < 0) {
            free(p);
            return NULL;
        }
        /* If that worked, return the string */
        if (n < size)
            return p;
        /* Else try again with more space */
        size = n + 1;       /* Precisely what is needed */
        np = realloc(p, size);
        if (np == NULL) {
            free(p);
            return NULL;
        } else {
            p = np;
        }
    }
}

バージョン 2.0.6 より前の glibc で切り詰めが起こった場合、切り詰めは適切に処理されず、エラーとして扱われる。

この文書について

この man ページは Linux man-pages プロジェクトのリリース 3.65 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。