文字列を数値に変換

文字列操作5

ほとんどのプログラミング言語では、文字と数値は別物として扱われます。
同じ「1」という値でも、文字としての「1」と数値としての「1」は別物なのです。

#include <stdio.h>

int main()
{
    char kazu = 1;
    char moji = '1';
    
    printf("kazu: %d\n", kazu);
    printf("moji: %c\n", moji);

    printf("\n");

    printf("kazu: %c\n", kazu);
    printf("moji: %d\n", moji);

    getchar();
}
kazu: 1
moji: 1

kazu: ・
moji: 49

このコードでは同じchar型の変数に、数値としての「1」と文字としての「1」を代入しています。
8、9行目はそれぞれ正しく出力されますが、13、14行目ではprintf関数の変換指定子をそれぞれ逆にしています。
一見同じ値でも、内部的には全く別の値が保存されていることがわかります。

ユーザーのキーボード入力やファイルからの文字の読み込みなどで得られるデータはすべて「文字」です。
ユーザーが「123」と数値を入力しても、そのままでは「123」という数値を得ることはできず、単純な足し算すらできません。

//こういうことはできない
char moji[] = "123" + "456";

atoi関数

数字を数値に変換するにはatoi関数を使用します。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char moji[] = "123";
    int kazu = atoi(moji);

    printf("%d", kazu);
    getchar();
}
int atoi(
 const char *str
);
strを数値に変換して返す。
変換に失敗した場合は0を返す。
(Visual Studioの場合)

atoi関数を使用するにはコードの先頭に「#include <stdlib.h>」を指定する必要があります。

atoi関数は引数の文字列が数値として読めるならば、数値にして返します。
数字以外の文字列を指定した場合の動作は不定ですが、Visual Studioの場合は0を返します。

数字と数字以外の文字が混在する場合は、先頭から数字と評価できる分だけを数字として返します。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char moji1[] = "12a3b";
    char moji2[] = "a123b";
    int kazu1 = atoi(moji1);
    int kazu2 = atoi(moji2);

    printf("%d\n", kazu1);
    printf("%d\d", kazu2);
    
    getchar();
}
12
0

このコードの「moji1」は先頭から2文字が数字なのでkazu1には「12」が代入されます。
「moji2」は途中に数字がありますが、先頭が数字ではないので変換できず、「kazu2」には「0」が代入されます。

atol、atoll、atof関数

同じような働きをする関数にatol関数atoll関数atof関数があります。
atol関数は戻り値をint型ではなくlong型を返します。
atoll関数はlong long型を返します。
atof関数はdouble型を返します。

戻り値のデータ型が異なる以外使い方は同じなので、サンプルコード等は省略します。

strtol関数

文字列を数値に変換するにはstrtol関数を使用することもできます。
atoi関数に比べて使い方がやや難しいものの、より安全かつ柔軟な変換が可能です。

long strtol(
 const char *strSource,
 char **endptr,
 int base
);
文字列strSourceをbase進数の文字列と解釈した値をlong型で返す。
ポインタendptrには読み取った次の文字への位置を格納する。
#include <stdio.h>
#include <stdlib.h>

int main()
{
	const char str[] = "123 456 7ab";
	char *end1, *end2, *end3;

	long num1 = strtol(str, &end1, 10);
	long num2 = strtol(end1, &end2, 10);
	long num3 = strtol(end2, &end3, 10);

	printf("%d\n", num1);
	printf("%d\n", num2);
	printf("%d\n", num3);

	printf("%s\n", end3);

	getchar();
}
123
456
7
ab

strtol関数の第一引数は変換対象となる文字列です。
先頭から文字列を読み取り、数値と解釈できない文字が表われるまでの文字列を数値に変換します。
先頭に空白文字がある場合は無視されます。
(何らかの文字が表われるまで読み飛ばされる)

第二引数はポインタという機能を学習しないと意味が分からないと思います。
とりあえず第二引数に渡す変数は、char型変数の変数名の前に「*」記号を付けて宣言し、実引数には「&」記号を指定する、と覚えてください。
必要がない場合はNULLを渡すことも可能です。
この引数については後述します。

第三引数は文字列の基数を指定します。
「10」を指定すれば文字列中の数値は10進数であると解釈され、「16」を指定すれば16進数と解釈されます。
2~36の数値が指定できます。

第三引数に「0」を指定すると、文字列の形式から自動的に判別します。
文字列の先頭が「0」ならば8進数、「0x」「0X」の場合は16進数、それ以外の場合は10進数と解釈されます。

#include <stdio.h>
#include <stdlib.h>

int main()
{
	long num1 = strtol("123", NULL, 10);
	long num2 = strtol("123", NULL, 8);
	long num3 = strtol("123", NULL, 16);

	long num4 = strtol("123", NULL, 0);
	long num5 = strtol("0123", NULL, 0);
	long num6 = strtol("0x123", NULL, 0);

	printf("%d\n", num1);
	printf("%d\n", num2);
	printf("%d\n", num3);

	printf("\n");

	printf("%d\n", num4);
	printf("%d\n", num5);
	printf("%d\n", num6);

	getchar();
}
123
83
291

123
83
291

※この実行結果はすべて10進数表記です。

第二引数endptr

第二引数endptrは「ポインタ」への「ポインタ」を指定します。

char *end;

long num = strtol("123", &end, 10);

ポインタに関する説明は省いて説明すると、上記の形式でchar型変数を宣言して第二引数に渡すと、この変数には「数値と解釈できない文字が表われた文字の場所」の情報が格納されます。
文字列をすべて数値と解釈できた場合は、終端のNULL文字の位置の情報が格納された状態となります。
この変数から文字を取り出すには変数名の前に「*」記号を付けます。

char *end1, *end2;

long num1 = strtol("abc", &end1, 10);
long num2 = strtol("123def", &end2, 10);

printf("%c\n", *end1);
printf("%c\n", *end2);
a
d

「abc」は数値(10進数)とは解釈できないので文字列の先頭である「a」の位置の情報が変数end1に格納されます。
「123def」は「123」までは数値と解釈でき、以降は数値とは解釈できないため「d」の位置の情報が変数end2に格納されます。

strtol関数は先頭の空白文字は読み飛ばすため、最初のサンプルコードのように、第二引数endptrを利用すると空白で区切られた文字列から複数の値を連続して変換することができます。

変換エラーへの対処その1

atoi関数は変換に失敗した時に0を返しますが、実はこれはコンパイラに依存します。
多くのコンパイラでは0を返すようですが、C言語の仕様では決められていません。

また、0が返ってきた場合、それは「変換に失敗して0が返ってきた」のか「文字列の0を変換して0を返したのか(つまり正常終了)」を判断することができません。
このためatoi関数は使用が推奨されていません。

strtol関数は第二引数のendptrを使用することで正常終了か否かを判断することができます。

#include <stdio.h>
#include <stdlib.h>

int main()
{
	const char str1[] = "0";
	const char str2[] = "a";

	char *end1, *end2;

	long num1 = strtol(str1, &end1, 10);
	long num2 = strtol(str2, &end2, 10);

	printf("%d\n", num1);
	printf("%d\n", num2);

	printf("\n");

	if(*end1 == '\0')
		printf("str1の変換は成功\n");
	else
		printf("str1の変換は失敗\n");

	if (*end2 == '\0')
		printf("str2の変換は成功\n");
	else
		printf("str2の変換は失敗\n");

	getchar();
}
0
0

str1の変換は成功
str2の変換は失敗

第二引数endptrは文字列の終端まで変換に成功すると終端のNULL文字の位置を指した状態となります。
つまりendptrとNULL文字が等しいかを比較することで、変換成功か失敗かを判別できます。

ただしこの方法では文字列の途中まで読み取れた場合に正しく判定できません。

char *end;

long num = strtol("0a", &end, 10);

printf("%d\n", num);

if (*end == '\0')
    printf("変換成功\n");
else
    printf("変換失敗\n");
0
変換失敗

実際には「0a」の先頭の「0」は変換できていますが失敗と表示されています。

以下のように、endptrとstrの「メモリ上の位置」同士を比較することで成功か失敗かを判定することは可能です。
メモリ上の位置が異なる場合、少なくとも一文字以上は読み取れたことになります。
(これの意味の理解にはポインタの知識が必要です)

const char str[] = "0a";
char *end;

long num = strtol(str, &end, 10);

printf("%d\n", num);

if (end != str)
    printf("変換成功\n");
else
    printf("変換失敗\n");
0
変換成功

変換エラーへの対処2

strtol関数はlong型以上の値は変換できず、これ以上の値を解釈しようとすると変換に失敗します。
このとき、errnoという特殊な値にERANGEという定数をセットします。

errnoはいくつかの標準関数で使用される、何らかのエラーが発生した場合にエラーの情報を格納する特殊な値です。
ただしエラーが発生してもerrnoを書き換えない関数もあります。
(atoi関数は書き換えません)
内部的にはint型で、プログラムの実行直後は何もエラーが発生していない状態を表す0がセットされています。

strtol関数でerrnoがERANGEにセットされる時、戻り値はLONG_MAXという定数となります。
LONG_MAXはlong型で扱える最大値を意味します。

erronoを使用するには<errno.h>をインクルードします。
LONG_MAXは<limits.h>をインクルードすると使用できます。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

int main()
{
	//long型の最大値より1大きい値
	const char str[] = "2147483648";

	long num = strtol(str, NULL, 10);
	if (errno == ERANGE)
	{
		printf("long型の最大値を超えた変換を行おうとしました。\n");
		
		//errnoを0に戻しておく
		errno = 0;
	}

	printf("%d\n", num);

	//参考:LONG_MAX
	printf("%d\n", LONG_MAX);

	getchar();
}
long型の最大値を超えた変換を行おうとしました。
2147483647
2147483647

strtoll、strtoul、strtoull関数

strtol関数はlong型を扱いますが、類似する関数にstrtoll関数strtoul関数strtoull関数があります。

strtoll関数はlong long型を扱います。
long long型の最大値を表す定数はLLONG_MAXです。

strtoul関数はunsigned long型を扱います。
unsigned long型の最大値を表す定数は ULONG_MAXです。

strtoull関数はunsigned long long型を扱います。
unsigned long long型の最大値を表す定数はULLONG_MAXです。

データ型が異なる以外は使用方法は同じなので、サンプルコード等は省略します。