【C言語】文字列を数値に変換する方法【危険なatoi関数、厳格なstrtol関数】

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)

変換関数一覧

変換関数定義
intatoiint atoi(const char *);
longatollong atol(const char *);
long longatolllong long atoll(const char *);
doubleatofdouble atof(const char *);

strto系(strtol, strtod, strtof)関数

より厳格な変換を行う場合にはatoi関数の代わりにstrtol関数を用いる必要があります。浮動小数点数を変換する場合にはstrtodstrtof関数を用います。

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型に対応した個別の関数を用いる必要があります。

変換関数定義
longstrtollong strtol(const char *, char **, int);
unsigned longstrtoulunsigned long strtoul(const char *, char **, int);
long longstrtoqlong long strtoq(const char *, char **, int)
unsigned long longstrtouqunsigned long long strtouq(const char *, char **, int);
floatstrtofdouble strtof(const char *, char **);
doublestrtoddouble strtod(const char *, char **);
long doublestrtoldlong 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%d10進符号付き整数
unsigned int%u10進符号無し整数
unsigned int%o, %x8進数, 16進数 符号無し整数
long%ldlong型のサイズが64bitの場合
float%f
double%lfprintfのフォーマットとは異なる

オーバーフロー・アンダーフロー

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 );
広告