C言語で文字列型の数字列を整数型や実数型の数値に変換する方法を紹介します。ato系の関数とstrto系の関数を用いる方法があります。厳格な変換を行う際にはstrto系の関数を用いる必要があります。より便利な方法としてsscanf関数を用いることもできます。
ato系(atoi, atol, atof)関数
文字列型からint型やdouble型の数値に変換する場合にはatoi
関数やatof
関数を用います。
int i = atoi("9");
double f = atof("3.1");
long l = atol("-1");
printf("%d, %f, %ld", i, f, l); // 9, 3.100000, -1
printf("%f", atof("1e3")); // 1000.000000
変換が行えなかった場合には値0
が返されます。ただしエラーが発生した場合の挙動は処理系依存の動作となるため注意が必要です。
/* 変換不可能の場合 */
atoi("a"); // 0 //
/* 整数オーバーフロー・アンダーフロー(int型の範囲外) */
atoi("2147483648"); // -2147483648 //
atoi("-2147483649"); // 2147483647 //
/* 整数オーバーフロー・アンダーフロー(unsigned int型の範囲外) */
atoi("4294967296"); // 0 //
atoi("4294967297"); // 1 //
/* 整数オーバーフロー・アンダーフロー(long型の範囲外) */
atoi("9223372036854775808"); // -1 //
atoi("-9223372036854775809"); // 0 //
このように、atoi関数の場合は変換値がint型で表現可能な値の範囲外となるようなケースと、変換対象の数字列がlong型の範囲外となるケースで、それぞれ不定な動作が取られます。
ato関数群に関する具体的な解説は以下の関数リファレンスが参考になります。
参考: atoi関数|ato関数群(atoi, atol, atoll, atof)
変換関数一覧
型 | 変換関数 | 定義 |
---|---|---|
int | atoi | int atoi(const char *); |
long | atol | long atol(const char *); |
long long | atoll | long long atoll(const char *); |
double | atof | double atof(const char *); |
strto系(strtol, strtod, strtof)関数
より厳格な変換を行う場合にはatoi関数の代わりにstrtol
関数を用いる必要があります。浮動小数点数を変換する場合にはstrtod
やstrtof
関数を用います。
long r = strtol("99yen", NULL, 10);
// r == 99
double r = strtod("3.14", NULL);
// r == 3.14
strtol("11", NULL, 2); // 3 ( 2進数として変換)
strtol("11", NULL, 8); // 9 ( 8進数として変換)
strtol("11", NULL, 16); // 17 (16進数として変換)
strtod("0x11", NULL); // 17 (16進数からの変換)
strtod("1e3", NULL); // 1000 (指数表記からの変換)
strtol関数の細かな仕様については、以下の関数リファレンスが参考になります。
参考: strtol関数|strto関数群(strtol, strtoul, strtoq, strtouq)
変換関数一覧
long型やint型以外の変換を行う場合には、strtodやstrtof等のdouble型やfloat型に対応した個別の関数を用いる必要があります。
型 | 変換関数 | 定義 |
---|---|---|
long | strtol | long strtol(const char *, char **, int); |
unsigned long | strtoul | unsigned long strtoul(const char *, char **, int); |
long long | strtoq | long long strtoq(const char *, char **, int) |
unsigned long long | strtouq | unsigned long long strtouq(const char *, char **, int); |
float | strtof | double strtof(const char *, char **); |
double | strtod | double strtod(const char *, char **); |
long double | strtold | long double strtold(const char *, char **) |
オーバーフロー/アンダーフローの検知
strtol関数は変換時のエラーやオーバーフロー/アンダーフローを検知することもできます。
long r = strtol("50000000000000000000", NULL, 10);
if (errno == ERANGE) {
puts("オーバーフローとアンダーフローのいずれかが発生しました");
}
if (errno == ERANGE && r == LONG_MAX) {
puts("オーバーフローが発生しました");
}
if (errno == ERANGE && r == LONG_MIN) {
puts("アンダーフローが発生しました");
}
文字列型からint型への変換
int型を返すstrtoi関数は存在しないため、strtolの結果をintにキャストして利用する必要があります。
// #include <stdlib.h> /* strtol */
// #include <limits.h> /* INT_MAX, INT_MAX */
const char *nptr = "yen";
char *endptr;
long r = strtol(nptr, &endptr, 10);
if (nptr == endptr) { // assert(r == 0);
puts("数値に変換できませんでした");
}
if (r > INT_MAX || r < INT_MIN) {
puts("int型には変換できません");
}
// 安全な場合のみキャスト
int i = (int)r;
sscanf関数
sscanf関数を活用することで、文字列をint/long型の整数値やfloat/double型の浮動小数点数に変換することもできます。
int i;
// 変換成功時
int n = sscanf("99yen", "%d", &i);
assert( i == 99 ); // 変換後の値
assert( n == 1 ); // 変換が行われた回数
i = 2;
// 変換無し
assert( 0 == sscanf("yen99", "%d", &i) );
assert( i == 2 );
// 失敗時(入力誤り)
assert( EOF == sscanf("", "%d", &i) );
assert( i == 2 );
// 浮動小数点数型
float f;
assert( 1 == sscanf("3.14yen", "%f", &f) );
assert( f == 3.14 );
// 複数
int a, b, c;
assert( 3 == sscanf("1-2,3", "%d%d,%d", &a, &b, &c) );
assert( a == 1 && b == -2 && c == 3 );
// 文字型への変換
char a, b, c;
sscanf("12yen", "%c%c%c", &a, &b, &c);
assert( a == '1' && b == '2' && c == 'y' );
戻り値
sscanf関数の戻り値は、変換が成功した回数(実引数への代入が成功した回数)が返されます。変換が一度も行われないまま入力誤りが発生した場合には、マクロ定数EOF
が返されます。
変換指定子
%d
, %f
以外の変換指定子を活用すれば、char型やlong型への数値変換や、8進数/16進数文字列からの数値変換も可能となります。
型名 | 変換指定子 | 備考 |
---|---|---|
char | %c | |
int | %d | 10進符号付き整数 |
unsigned int | %u | 10進符号無し整数 |
unsigned int | %o, %x | 8進数, 16進数 符号無し整数 |
long | %ld | long型のサイズが64bitの場合 |
float | %f | |
double | %lf | printfのフォーマットとは異なる |
オーバーフロー・アンダーフロー
sscanf関数による数値変換では、値が表現可能な値の範囲外となるケースの検知が行えない点に注意が必要です。整数オーバーフロー/アンダーフローが発生した場合の動作は処理系依存となります。
/* Clang/LLVM コンパイラの場合 */
int i = 9; // 2147483647 == INT_MAX
assert( 1 == sscanf("2147483648", "%d", &i) );
assert( i == -2147483648 );
assert( 0 == errno );
int j = 9; // -2147483648 == INT_MIN
assert( 1 == sscanf("-2147483649", "%d", &j) );
assert( j == 2147483647 );
assert( 0 == errno );
unsigned int u = 9;// 4294967295 == UINT_MAX
assert( 1 == sscanf("4294967296", "%u", &u) );
assert( u == 0 );
assert( 0 == errno );