sprintf関数

文字列操作6

文字列を数値に変換するには、atoi関数やstrtol関数を使用すると説明しました。
(文字を数値に変換参照)
では、その逆はどうすればよいのでしょうか。

実はC言語の標準関数ではこの機能を持った関数はありません。
コンパイラによっては「atoi」の逆である「itoa」という関数が存在することがありますが、常に使えるわけではないので使用は推奨されていません。
(Visual C++では_itoaという名前で使用可能です)

これを実現するにはsprintf関数、snprintf関数、またはsprintf_s関数、snprintf_s関数を使用します。
これらは非常に強力な文字列操作関数で、strcat(文字列結合)やstrcpy(文字列コピー)などもこれで代用が可能です。

sprintf_s関数、snprintf_s関数はコンパイラによっては使用できません。

sprintf関数はVisualStudio既定の設定では使用するとエラーとなります。
詳しくは_s系関数とエラー表示についてを参照してください。

sprintf、snprintf関数

int sprintf(
 char *buffer,
 const char *format [,
 argument] ...
);
文字列配列bufferに、書式指定文字列formatに従って変換した文字列を格納する。
戻り値は書き込まれた文字数。
エラーが発生した場合は負数を返す。
int snprintf(
 char *buffer,
 size_t count,
 const char *format [,
 argument] ...
);
文字列配列bufferに、書式指定文字列formatに従って変換した文字列をcount文字分格納する。
戻り値は書き込まれた文字数。
エラーが発生した場合は負数を返す。

#include <stdio.h>

//文字列配列のサイズ
#define BUFFER 256

int main()
{
	char buf1[BUFFER];
	char buf2[BUFFER];

	const char str1[] = "名前";
	const char str2[] = "年齢";
	const char str3[] = "身長";

	const char name[] = "〇山×男";
	int age = 20;
	double height = 170.5;

	sprintf(
		buf1,
		"%s: %s\n%s: %d\n%s: %.1f\n",
		str1, name,
		str2, age,
		str3, height
	);

	snprintf(
		buf2,
		BUFFER,
		"%s: %s\n%s: %d\n%s: %.1f\n",
		str1, name,
		str2, age,
		str3, height
	);

	printf("%s\n", buf1);
	printf("%s\n", buf2);

	getchar();
}
名前: 〇山×男
年齢: 20
身長: 170.5

名前: 〇山×男
年齢: 20
身長: 170.5

これらの関数はprintf関数と非常に近い動作をします。
書式指定文字列も同じものが使用できます。
printf関数は結果の文字列を標準出力に出力しますが、sprintf系の関数は結果の文字列を別のchar型配列に出力します。

sprintf関数はコピー先配列のサイズの指定がなく、サイズが足りない場合にバッファオーバーランが発生するので使用は避けるべきです。

snprintf関数は第二引数sizeOfBufferでコピー先配列のサイズを指定できます。
この値よりもコピーされる文字列のサイズが大きい場合、文字列は切り捨てられます。
この時必ず配列の終端にNULL文字が付加されるため、実際にコピーできる文字数は「sizeOfBuffer - 1」までとなります。


#include <stdio.h>

int main()
{
	char buf1[5], buf2[10];

	snprintf(buf1, sizeof(buf1), "ABCDEFG");
	snprintf(buf2, sizeof(buf2), "ABCDEFG");

	printf("%s\n", buf1);
	printf("%s\n", buf2);

	getchar();
}
ABCD
ABCDEFG

sprintf_s、snprintf_s関数

int sprintf_s(
 char *buffer,
 size_t sizeOfBuffer,
 const char *format,
 ...
);
文字列配列bufferに、書式指定文字列formatに従って変換した文字列をsizeOfBuffer文字分格納する。
戻り値は書き込まれた文字数。
エラーが発生した場合は負数を返す。
int snprintf_s(
 char *buffer,
 size_t sizeOfBuffer,
 const char *format,
 ...
);
文字列配列bufferに、書式指定文字列formatに従って変換した文字列をsizeOfBuffer文字分格納する。
戻り値は書き込まれた文字数。
エラーが発生した場合は負数を返す。

snprintf_s関数は上記のような定義ですが、当方のテスト環境にこの関数が使えるコンパイラがありません。
VisualStudioでは以下の_snprintf_s関数ができますので、代わりに紹介します。

int _snprintf_s(
 char *buffer,
 size_t sizeOfBuffer,
 size_t count,
 const char *format [,
 argument] ...
);
文字列配列buffer(サイズはsizeOfBuffer)に、書式指定文字列formatに従って変換した文字列をcount文字分格納する。
戻り値は書き込まれた文字数。
エラーが発生した場合は負数を返す。

#include <stdio.h>

//文字列配列のサイズ
#define BUFFER 256

int main()
{
	char buf1[BUFFER];
	char buf2[BUFFER];

	const char str1[] = "名前";
	const char str2[] = "年齢";
	const char str3[] = "身長";

	const char name[] = "〇山×男";
	int age = 20;
	double height = 170.5;

	sprintf_s(
		buf1,
		BUFFER,
		"%s: %s\n%s: %d\n%s: %.1f\n",
		str1, name,
		str2, age,
		str3, height
	);

	_snprintf_s(
		buf2,
		BUFFER,
		BUFFER,
		"%s: %s\n%s: %d\n%s: %.1f\n",
		str1, name,
		str2, age,
		str3, height
	);

	printf("%s\n", buf1);
	printf("%s\n", buf2);

	getchar();
}
名前: 〇山×男
年齢: 20
身長: 170.5

名前: 〇山×男
年齢: 20
身長: 170.5

sprintf_s関数はコピー先配列のサイズが足りずに文字列の切り捨てが発生するとエラーを発生させ、プログラムを停止します。

_snprintf_s関数は第三引数countでコピーする文字数を制限できます。
終端にNULL文字を付加するので「コピー先配列サイズ-1」を指定すると安全にコピーができます。
_TRUNCATEという定数を指定すると同様の動作となります。


#include <stdio.h>

int main()
{
	char buf1[5], buf2[5], buf3[5];

	//エラー発生
	//sprintf_s(buf1, sizeof(buf1), "ABCDEFG");

	_snprintf_s(buf2, sizeof(buf2), sizeof(buf2) - 1, "ABCDEFG");
	_snprintf_s(buf3, sizeof(buf3), _TRUNCATE, "ABCDEFG");

	//printf("%s\n", buf1);
	printf("%s\n", buf2);
	printf("%s\n", buf3);

	getchar();
}
ABCD
ABCD

複数の文字列を結合したい場合はstrcat関数よりもsprintf系の関数のほうが簡単に書けるでしょう。
文字、文字列以外の変数を文字列に変換したい場合にも重宝する関数です。

ちなみに、double型変数のheightを表示するための変換指定子を%.1fとしています。
これは小数点の第一位までを表示する、という指定になります。

例えば小数点第三位まで表示したいのならば%.3fと記述します。
今までは%fと、特に桁数の指定をしていませんでしたが、この場合は自動的に%.6fを指定したことになります。

printf関数の書式指定文字列について詳しくはprintf関数を参照してください。