このページでは、C言語における “文字列” を “数値” に変換する方法について解説していきたいと思います。
この変換をよく利用するのは “コマンド引数で受け取った文字列を数値で扱いたい” 時だと思います。
下記のように main
関数を定義することで、プログラム実行時に引数として文字列を受け取ることができるようになります。
int main(int argc, char *argv[]) {
printf("%ld\n", argv[1] * 3);
return 0;
}
例えばコマンド引数で 12345
という数値を指定してプログラムを実行したとしても、main
関数では argv[1]
に "12345"
という「文字列」が格納されることになります(より正確には argv[1]
はポインタなので、"12345"
が格納されたメモリのアドレスが格納される。さらに文字列の最後は必ずヌル文字 '\0'
となる)。
"12345"
という文字列なので、このままだと数値として演算に利用することはできません。例えば上記のプログラムでは文字列に対して * 3
の演算を行おうとしているので、コンパイル時にエラーが発生してしまいます。
文字列を数値として演算に利用したい場合は、事前にこの文字列を数値に変換しておく必要があります。
このような “数値として扱いたい文字列” を数値に変換する時などに、このページで紹介する方法は役に立つと思います。
ちなみに上記の例では文字列に対して掛け算を行っているのでコンパイルエラーになりますが、足し算や引き算を行うことは可能です
ただし、C言語における文字列とは、要は文字列の格納されたメモリを指す “ポインタ” ですので、この場合の足し算や引き算はアドレスへの足し算・引き算を行う処理となります
なので、単にポインタの指す先が変わるだけであり、文字列を数値として扱った場合の演算結果とは異なるので注意してください
例えば文字列 char * str = "12345";
に対して + 2
した場合、str
の指す先が '3'
の位置のアドレスになるだけで、演算結果が 12347
になるというわけではありません
また、今回は “文字列” を数値に変換する方法について解説していきますが、”文字”(数字1文字)を数値に変換する方法については下記ページで解説していますので、こちらも知りたい方は合わせて読んでみてください。
【C言語】数字⇔数値の変換方法Contents
atoi
・atol
・atof
などを利用する
文字列を数値に変換する方法として最も有名で簡単なのが atoi
や atol
、atof
などといった atoi
系の関数を利用することだと思います。
atoi
・atol
・atof
とは
これらの atoi
系の関数は全て文字列を数値に変換してくれる関数になります。
各関数は stdlib.h
において下記のよう宣言されています。
int atoi(const char *);
long atol(const char *);
double atof(const char *);
要は、引数として文字列を渡して実行することで、その文字列を数値に変換した結果を取得することができる関数群になります。
これらは返却するデータの型が異なりますので、扱いたい数値に応じて使い分けする必要があります。
また、他にも環境によっては long long
型に変換する atoll
が使用できる場合もあります。
このページではこれらの関数をまとめて atoi
系の関数と呼ばせていただきます。
atoi
系関数の使用例
例えば文字列を long
型の数値に変換したい場合は下記のように atol
関数を実行します。
#include <stdio.h>
#include <stdlib.h> /* atolを使用するため */
int main(void) {
char *text = "12345";
long num;
/* 文字列を数値に変換 */
num = atol(text);
/* 数値を用いて計算を実行して結果を表示 */
printf("%ld\n", num * 3);
return 0;
}
実行すると下記のように文字列 "12345"
が数値 12345
に変換され、その数値を 3
倍した結果が表示されます。
37035
このように計算が実行できているのは、文字列ではなく変換後の数値を利用しているためです。文字列のまま * 3
を行うとコンパイル時にエラーが発生します。
例えば私の環境では下記のようなエラーが発生しました。
error: invalid operands to binary expression ('char *' and 'int')
スポンサーリンク
atoi
系の関数での文字列の変換例
atoi
系の関数で、どのような文字列がどのような値に実際に変換されるかの例を紹介していきたいと思います。
文字がすべて数字の場合
まず、文字列の全ての文字が数字の場合は、基本的には文字列はそのまま数値に変換されます。
atoi("12345") → 12345
atol("12345") → 12345
atof("12345") → 12345.0
文字数が多くてオーバーフローする場合
ただし、文字列の文字数が多すぎて数値への変換時にオーバーフローが発生した時は正しく変換することはできません。浮動小数点数に変換する際には誤差が発生する場合もあります。
atoi("11111111111111111111") → -1
atol("11111111111111111111") → 9223372036854775807
atof("11111111111111111111") → 11111111111111110656.0
不正な文字がある場合
文字列の中に不正な文字(数値に変換できない文字)が存在する場合、その不正な文字以降の文字の変換は行われません。
atoi("12x45") → 12
atol("12x45") → 12
atof("12x45") → 12.0
もし文字列の “先頭” が不正な文字の場合は 0
に変換されます。
atoi("x2345") → 0
atol("x2345") → 0
atof("x2345") → 0.0
ピリオドがある場合
数値に変換できない文字でも、浮動小数点数に変換する際はピリオド(.
)に関しては小数点として変換されます。整数に変換する際はピリオド以降は変換されません。
atoi("12.34") → 12
atol("12.34") → 12
atof("12.34") → 12.34
ただし、複数ピリオド(.
)が存在する場合は、浮動小数点数に変換する際にも2つ目のピリオド以降は変換されません。
atoi("12.34.56") → 12
atol("12.34.56") → 12
atof("12.34.56") → 12.34
先頭が +
や -
の場合
文字列の先頭に限り、+
や -
により数値の符号を指定することができます。
atoi("-1234") → -1234
atol("-1234") → -1234
atof("-1234") → -1234.0
atoi
系の関数を利用するメリット
atoi
系の関数を利用する1番のメリットは「簡単に使える」ところだと思います。
引数が “文字列” 1つのみのシンプルな関数なので、文字列さえ用意すればあとは簡単に数値に変換することができます。
atoi
系の関数を利用するデメリット
ただし、atoi
系の関数では「エラー処理ができない」というデメリットがあります。
atoi
系の関数では、どんな文字列を引数に指定したとしても何らかの数値に変換されてしまいます。要は、atoi
系の関数は基本的にエラーが発生しません。
例えば、文字列 "abcd"
を引数に指定した場合、関数実行の結果として得られる数値は 0
になります。つまり、文字列 "0"
を渡された時と同じ結果になります。
なので、ユーザーから指定された文字列を数値に変換するような場合、ユーザーが数字以外を含む文字列を指定したとしても、atoi
系の関数ではそれを検知することができません。
したがって、ユーザーから数字以外を含んだ文字列を指定された場合に、エラーであることをユーザーに伝えたいと思っても、atoi
系の関数だけだとこれが実現できません。なぜなら、ここまで説明したように atoi
系の関数ではエラーが発生しないのでエラーを検知する(文字列に数字以外が含まれていることを検知する)ことができないからです。
ただし、atoi
系の関数のみでエラーを検知できないだけで、他の方法でエラーを検知するようにすれば、上記のようなユーザーにエラーであることを伝えるようなことも可能になります。
例えば下記だと、atoi
系の関数を実行する前に i
に対するループの中で文字列の全ての文字が数字であるかどうかを判断しており、この判断のところでエラーを検知することが可能になります(先頭が +
や -
でもエラーになってしまうので本当はこの判断では不十分です…)。
#include <stdio.h>
#include <stdlib.h> /* atolを使用するため */
#include <string.h> /* strlenを使用するため */
int main(int argc, char *argv[]) {
int i;
int len;
long num;
if (argc != 2) {
printf("引数を指定してください\n");
return -1;
}
/* 文字列の長さを取得する */
len = strlen(argv[1]);
/* 1文字ずつ数字であるかどうかを判断していく */
for (i = 0; i < len; i++) {
/* argv[1][i]がアスキーコード的に'0'〜'9'の文字であるかを判断 */
if (argv[1][i] < '0' || argv[1][i] > '9') {
printf("指定された文字列に数字以外含まれています!\n");
return -1;
}
}
/* 文字列を数値に変換 */
num = atol(argv[1]);
/* 数値を用いて計算を実行して結果を表示 */
printf("%ld\n", num * 3);
return 0;
}
ただ、文字列変換時にエラー検知を行いたいのであれば次に説明する strtol
系の関数を利用する方が簡単です。
スポンサーリンク
strtol
・strtod
などを利用する
続いて紹介するのが strtol
や strtod
といった strtol
系の関数を利用して文字列を数値に変換する方法です。
同じような名前の関数として strtok
が存在しますが、この関数は strtol
系の関数とは全く動作が異なるので注意してください。
strtol
・strtod
とは
strtol
系の各関数は stdlib.h
において下記のよう宣言されています(string.h
ではないので注意してください)。
long strtol(const char *s, char **endptr, int base);
double strtod(const char *s, char **endptr);
実行すると、第1引数で指定した文字列を数値に変換した結果が返却されます。
それぞれで変換後のデータの型が異なるので、変換したいデータの型に合わせて関数を使い分ける必要があります。
他にも unisnged long
型に変換する strtoul
や float
型に変換する strtof
なども存在します。
ただし int
型に変換する関数は用意されていないので注意してください。int
型に変換したい場合は、strtol
で long
型に変換し、さらに long
型の数値をキャストして int
型に変換する必要があります。
このページではこれらの関数をまとめて strtol
系の関数と呼ばせていただきます。
引数
strtol
には3つの引数を指定して実行します。実行すると、第1引数の文字列を long
型の数値に変換した結果が返却されます。
- 第1引数
s
:数値に変換したい文字列 - 第2引数
endptr
:”最初に現れた不正な文字を指すポインタ” のアドレス - 第3引数
base
:基数(文字列を何進数の数値として扱うか)
strtod
には2つの引数を指定します。実行すると、第1引数の文字列を double
型の数値に変換した結果が返却されます。
- 第1引数
s
:数値に変換したい文字列 - 第2引数
endptr
:”最初に現れた不正な文字を指すポインタ” のアドレス
strtol
系の “整数に変換する” 関数では、第3引数の base
を指定することで、文字列を何進数の数字として扱うかを指定することができます。一方で、 strtod
などの “浮動小数点数に変換する” 関数では、base
を指定することはできません。
base
には 0
もしくは 2
〜 36
の値を設定することができます。
endptr
ややこしいのが第2引数の endptr
だと思いますので、これについて詳細を解説しておきます。
まず strtol
や strtod
の第2引数には “char *
型のポインタ変数” のアドレスを指定します。この char *
型のポインタ変数の変数名を、ここでは end
としたいと思います。
strtol
や strtod
を実行する時点では、このポインタ変数 end
にはどんなアドレスが格納されていても問題ありません(どこを指していても良い)。
ただし、第2引数の endptr
に指定するのは end
そのものではなく、end
のアドレスである必要があるところに注意が必要です。つまり、第2引数には &end
を指定します。このアドレスが strtol
や strtod
で引数 endptr
として使用されます。
このように指定して strtol
や strtod
を実行すると、実行後に end
に “文字列を先頭から数値に変換していく上で最初に見つかった不正な文字” へのアドレスが格納されることになります。
“不正な文字” とは数値に変換できない文字です。例えば "12345xy"
という文字列を strtol
で数値に変換しようとしても "xy"
は数字ではないので数値に変換できません。こういった文字が “不正な文字” となります。
で、この場合は end
に “最初に見つかった不正な文字”、つまり "12345xy"
の文字列の 'x'
を指すアドレスが格納されることになります。
さらに end
が指すアドレスに格納されているデータを取得する *end
は、'x'
となります。
では、”不正な文字が存在しない” 文字列を第1引数として strtol
系の関数を実行した場合、実行後の end
にはどんなアドレスが格納されるでしょうか?
結論としては end
には第1引数の文字列の終端文字(ヌル文字 '\0'
)のアドレスが格納されることになります。
まず前提として、C言語で扱う文字列の最後には必ずヌル文字 '\0'
が存在します。
そして、不正な文字が存在しない場合、文字列の先頭からヌル文字 '\0'
の前までの文字は全て数値に変換できる文字(数字や16進数の ABCDEF など)であるといえます。
なので、文字列の先頭からヌル文字 '\0'
の前までの文字は全て数値に変換されます。ですが、ヌル文字 '\0'
は数値には変換できない文字なので、不正な文字として扱われます。さらに、ヌル文字 '\0'
の前には不正な文字はないので、このヌル文字 '\0'
が “最初に見つかった不正な文字” となり、このヌル文字 '\0'
のアドレスが end
に格納されることになります。
逆に文字列の中に不正な文字が存在する場合は、strtol
系の関数を実行後に end
がヌル文字 '\0'
を指していることはあり得ません。ヌル文字以外の不正な文字を指していることになります。
“最初に見つかった不正な文字” のアドレスを別に知らなくても良い場合には、strtol
系の関数の第2引数には NULL
を指定すれば良いです。
endptr
を利用したエラーの検知
strtol
系の関数の第2引数で、”最初に見つかった不正な文字” のアドレスを知ることができて何が嬉しいのかというと、これはエラー処理ができるようになる点です。
前述の通り、文字列を数値に変換するにあたって、第1引数で指定する文字列に不正な文字が “存在する / 存在しない” 場合とで第2引数で指定したアドレスの char *
型のポインタ変数が挿す文字に下記のような違いがあります。
- 不正な文字が存在する場合:
'\0'
以外の文字 - 不正な文字が存在しない場合:
'\0'
したがって、例えば第2引数の endptr
に char *
型のポインタ変数である end
のアドレスを指定した場合、end
の指す先のデータ、つまり *end
のデータによって下記のように文字列に不正な文字があったことを判断することができます。
*end != '\0'
:文字列の中に不正な文字があった*end == '\0'
:文字列の中に不正な文字はなかった
そのため、文字列の中に不正な文字があった場合はエラーにしたいような場合は、上記の判断を行うだけで、そのエラー処理を実現することが可能です。
strtol
系の関数の使用例
下記は strtol
関数で文字列を long
型の数値に変換する例になります。
#include <stdio.h>
#include <stdlib.h> /* strtolを使用するため */
int main(void) {
char *text = "12x45";
long num;
char *end;
/* 文字列を数値に変換 */
num = strtol(text, &end, 10);
if (*end != '\0') {
printf("エラー!不正な文字が含まれています!\n");
return -1;
}
/* 数値を用いて計算を実行して結果を表示 */
printf("%ld\n", num * 3);
return 0;
}
上記の例では text
に不正な文字 'x'
が含まれるので、実行すると エラー!不正な文字が含まれています!
が表示されます。
strtol
系の関数での文字列の変換例
続いて strtol
系の関数で、どのような文字列がどのような値に実際に変換されるかの例を紹介していきたいと思います。
ただし、文字列を10進数として扱う場合、基本的な変換方針は atoi
系と同じようです。なので、特に strtol
系関数への基数の指定によって(第3引数 base
に指定した値によって)、変換結果がどのようなものになるのかに焦点を当てて紹介していきたいと思います。
すべての文字が数字の場合
まず文字列に数字以外の文字が存在しない場合でも、基数 base
の値によって変換結果が変化します。
strtol("123", NULL, 8) → 83
strtol("123", NULL, 16) → 291
不正な文字がある場合
また、atoi
系の関数同様に不正な文字(数値に変換できない文字)以降の文字は変換されません。
ただし、strtol
系の関数では基数 base
の指定によって、数字も不正な文字として扱われたり、数字以外も有効な文字として扱われることがあるので注意が必要です。
例えば文字列 "385A1"
で考えると、base
が 8
の場合は数字でも '8'
と '9'
は不正な文字として扱われます。なので、strtol
関数では文字列の "3"
の部分のみが8進数として扱われ数値に変換されます。
一方で、base
が 16
の場合は数字以外でも 'A'
〜 'F'
(小文字でも良い)は有効な文字として扱われます。なので、strtol
関数では文字列の "385A1"
の部分全体が16進数の数値に変換されます。
strtol("385A1", NULL, 8) → 8
strtol("385A1", NULL, 16) → 230817
さまざまな基数を指定した場合
strtol
関数の第3引数の base
には 2
〜 36
の値を設定可能で、2
を指定した場合は文字列を2進数として扱います。この場合、'0'
と '1'
以外の文字は全て不正な文字として扱われます(先頭の +
・-
を除いて)。
strtol("1001", NULL, 2) → 9
strtol("2001", NULL, 2) → 0
また、36
を指定した場合は '0'
〜 '9'
だけでなく、'A'
〜 'Z'
(小文字でも良い)のすべてのアルファベットも有効な文字として扱われます。
strtol("ZyxW", NULL, 36) → 1678244
base
に 1
や 37
以上の値を指定した場合は、変換結果はすべて 0
になるようです。
strtol("123", NULL, 37) → 0
基数に 0
を指定した場合
base
には 0
を指定することも可能で、この場合は第1引数の文字列の先頭部分によって文字列を何進数のものとして扱うかが決まります。
より具体的には、第1引数の文字列の先頭部分によって、下記のように文字列を何進数のものとして扱うかが決まります。
"0x"
or"0X"
から始まる場合:16進数- ↑ 以外で、
"0"
から始まる場合:8進数 - 上記以外の場合:10進数
strtol("0x123", NULL, 0) → 291
strtol("0123", NULL, 0) → 83
strtol("123", NULL, 0) → 123
スポンサーリンク
strtol
系の関数を利用するメリット
strtol
系の関数を利用するメリットはエラー処理を簡単に行うことができることです。
atoi
系の関数では、前述の通り単体ではエラーの判断(文字列に数値に変換できない不正な文字が存在するかどうかの判断)を行うことができません。エラーの判断を行うためには自身で文字列をチェックするような処理を実装する必要があります。
一方で strtol
系の関数であれば、これも前述の通り、第2引数に指定するアドレスのポインタの指すデータが '\0'
であるかどうかを確認するだけでエラーかどうかを判断することができます。
ですので、エラーの判断を行い、エラーである場合にエラー処理を行いたい場合は、文字列の数値への変換には strtol
系の関数を利用した方が良いと思います。
strtol
系の関数を利用するデメリット
デメリットは引数がちょっと複雑というところくらいかなぁと思います。
特に第2引数がダブルポインタ型になっているので難しそうと感じる方もいるかもしれませんが、要は char *
型のポインタを変数宣言し、そのポインタのアドレスを引数として指定すれば良いだけです。
/* char * 型のポインタを変数宣言 */
char *end;
/* 略 */
/* ポインタのアドレスを引数に指定 */
num = strtol(text, &end, 10);
if (*end != '\0') {
/* エラー処理 */
}
まとめ
このページではC言語における “文字列を数値に変換する方法” の解説を行いました!
主に atoi
系の関数と strtol
系の関数による変換方法を紹介をしましたが、これらの大きな違いは文字列に “不正な文字が含まれていることが判断可能かどうか” です。
とりあえずサクッと文字列を数値に変換したいのであれば atoi
系の関数を使えば良いですし、文字列に不正な文字が含まれていないかどうかをしっかり判断したいような場合は strtol
系の関数を使うのが良いと思います!
特にコマンド引数から受け取った文字列を数値として扱いたい場合に重宝しますので、atoi
系と strtol
系の関数があることはしっかり覚えておきましょう!