【C言語】文字列を数値に変換する方法(atoi・strtol など)

文字列の数値への変化方法の解説ページアイキャッチ

このページでは、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 の演算を行おうとしているので、コンパイル時にエラーが発生してしまいます。

文字列に対して演算を行う様子

文字列を数値として演算に利用したい場合は、事前にこの文字列を数値に変換しておく必要があります。

数値に変換後に演算を行う様子

このような “数値として扱いたい文字列” を数値に変換する時などに、このページで紹介する方法は役に立つと思います。

MEMO

ちなみに上記の例では文字列に対して掛け算を行っているのでコンパイルエラーになりますが、足し算や引き算を行うことは可能です

ただし、C言語における文字列とは、要は文字列の格納されたメモリを指す “ポインタ” ですので、この場合の足し算や引き算はアドレスへの足し算・引き算を行う処理となります

なので、単にポインタの指す先が変わるだけであり、文字列を数値として扱った場合の演算結果とは異なるので注意してください

例えば文字列 char * str = "12345"; に対して + 2 した場合、str の指す先が '3' の位置のアドレスになるだけで、演算結果が 12347 になるというわけではありません

atoiatolatof などを利用する

文字列を数値に変換する方法として最も有名で簡単なのが atoiatolatof などといった atoi 系の関数を利用することだと思います。

atoiatolatof とは

これらの atoi 系の関数は全て文字列を数値に変換してくれる関数になります。

各関数は stdlib.h において下記のよう宣言されています。

atoi系の関数
int atoi(const char *);
long atol(const char *);
double atof(const char *);

要は、引数として文字列を渡して実行することで、その文字列を数値に変換した結果を取得することができる関数群になります。

これらは返却するデータの型が異なりますので、扱いたい数値に応じて使い分けする必要があります。

また、他にも環境によっては long long 型に変換する atoll が使用できる場合もあります。

このページではこれらの関数をまとめて atoi 系の関数と呼ばせていただきます。

atoi 系関数の使用例

例えば文字列を long 型の数値に変換したい場合は下記のように atol 関数を実行します。

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系関数の変換例1
atoi("12345") → 12345
atol("12345") → 12345
atof("12345") → 12345.0

文字数が多くてオーバーフローする場合

ただし、文字列の文字数が多すぎて数値への変換時にオーバーフローが発生した時は正しく変換することはできません。浮動小数点数に変換する際には誤差が発生する場合もあります。

atoi系関数の変換例2
atoi("11111111111111111111") → -1
atol("11111111111111111111") → 9223372036854775807
atof("11111111111111111111") → 11111111111111110656.0

不正な文字がある場合

文字列の中に不正な文字(数値に変換できない文字)が存在する場合、その不正な文字以降の文字の変換は行われません。

atoi系関数の変換例3
atoi("12x45") → 12
atol("12x45") → 12
atof("12x45") → 12.0

もし文字列の “先頭” が不正な文字の場合は 0 に変換されます。

atoi系関数の変換例5
atoi("x2345") → 0
atol("x2345") → 0
atof("x2345") → 0.0

ピリオドがある場合

数値に変換できない文字でも、浮動小数点数に変換する際はピリオド(.)に関しては小数点として変換されます。整数に変換する際はピリオド以降は変換されません。

atoi系関数の変換例6
atoi("12.34") → 12
atol("12.34") → 12
atof("12.34") → 12.34

ただし、複数ピリオド(.)が存在する場合は、浮動小数点数に変換する際にも2つ目のピリオド以降は変換されません。

atoi系関数の変換例7
atoi("12.34.56") → 12
atol("12.34.56") → 12
atof("12.34.56") → 12.34

先頭が +- の場合

文字列の先頭に限り、+- により数値の符号を指定することができます。

atoi系関数の変換例7
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 系の関数を利用する方が簡単です。

スポンサーリンク

strtolstrtod などを利用する

続いて紹介するのが strtolstrtod といった strtol 系の関数を利用して文字列を数値に変換する方法です。

同じような名前の関数として strtok が存在しますが、この関数は strtol 系の関数とは全く動作が異なるので注意してください。

strtolstrtod とは

strtol 系の各関数は stdlib.h において下記のよう宣言されています(string.h ではないので注意してください)。

strtol系の関数
long strtol(const char *s, char **endptr, int base);
double strtod(const char *s, char **endptr);

実行すると、第1引数で指定した文字列を数値に変換した結果が返却されます。

それぞれで変換後のデータの型が異なるので、変換したいデータの型に合わせて関数を使い分ける必要があります。

他にも unisnged long 型に変換する strtoulfloat 型に変換する strtof なども存在します。

ただし int 型に変換する関数は用意されていないので注意してください。int 型に変換したい場合は、strtollong 型に変換し、さらに 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 もしくは 236 の値を設定することができます。

endptr

ややこしいのが第2引数の endptr だと思いますので、これについて詳細を解説しておきます。

まず strtolstrtod の第2引数には “char * 型のポインタ変数” のアドレスを指定します。この char * 型のポインタ変数の変数名を、ここでは end としたいと思います。

strtolstrtod を実行する時点では、このポインタ変数 end にはどんなアドレスが格納されていても問題ありません(どこを指していても良い)。

ただし、第2引数の endptr に指定するのは end そのものではなく、end のアドレスである必要があるところに注意が必要です。つまり、第2引数には &end を指定します。このアドレスが strtolstrtod で引数 endptr として使用されます。

このように指定して strtolstrtod を実行すると、実行後に 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引数の endptrchar * 型のポインタ変数である end のアドレスを指定した場合、end の指す先のデータ、つまり *end のデータによって下記のように文字列に不正な文字があったことを判断することができます。

  • *end != '\0':文字列の中に不正な文字があった
  • *end == '\0':文字列の中に不正な文字はなかった

そのため、文字列の中に不正な文字があった場合はエラーにしたいような場合は、上記の判断を行うだけで、そのエラー処理を実現することが可能です。

strtol 系の関数の使用例

下記は strtol 関数で文字列を long 型の数値に変換する例になります。

strtolの利用例
#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系関数の変換例1
strtol("123", NULL, 8) → 83
strtol("123", NULL, 16) → 291

不正な文字がある場合

また、atoi 系の関数同様に不正な文字(数値に変換できない文字)以降の文字は変換されません。

ただし、strtol 系の関数では基数 base の指定によって、数字も不正な文字として扱われたり、数字以外も有効な文字として扱われることがあるので注意が必要です。

例えば文字列 "385A1" で考えると、base8 の場合は数字でも '8''9' は不正な文字として扱われます。なので、strtol 関数では文字列の "3" の部分のみが8進数として扱われ数値に変換されます。

一方で、base16 の場合は数字以外でも 'A''F'(小文字でも良い)は有効な文字として扱われます。なので、strtol 関数では文字列の "385A1" の部分全体が16進数の数値に変換されます。

strtol系関数の変換例2
strtol("385A1", NULL, 8) → 8
strtol("385A1", NULL, 16) → 230817

さまざまな基数を指定した場合

strtol 関数の第3引数の base には 236 の値を設定可能で、2 を指定した場合は文字列を2進数として扱います。この場合、'0''1' 以外の文字は全て不正な文字として扱われます(先頭の +- を除いて)。

strtol系関数の変換例3
strtol("1001", NULL, 2) → 9
strtol("2001", NULL, 2) → 0

また、36 を指定した場合は '0''9' だけでなく、'A''Z'(小文字でも良い)のすべてのアルファベットも有効な文字として扱われます。

strtol系関数の変換例4
strtol("ZyxW", NULL, 36) → 1678244

base137 以上の値を指定した場合は、変換結果はすべて 0 になるようです。

strtol系関数の変換例5
strtol("123", NULL, 37) → 0

基数に 0 を指定した場合

base には 0 を指定することも可能で、この場合は第1引数の文字列の先頭部分によって文字列を何進数のものとして扱うかが決まります。

より具体的には、第1引数の文字列の先頭部分によって、下記のように文字列を何進数のものとして扱うかが決まります。

  • "0x" or "0X" から始まる場合:16進数
  • ↑ 以外で、"0" から始まる場合:8進数
  • 上記以外の場合:10進数
strtol系関数の変換例6
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 * 型のポインタを変数宣言し、そのポインタのアドレスを引数として指定すれば良いだけです。

第2引数の指定の仕方
/* char * 型のポインタを変数宣言 */
char *end;

/* 略 */

/* ポインタのアドレスを引数に指定 */
num = strtol(text, &end, 10);
if (*end != '\0') {
    /* エラー処理 */
}

まとめ

このページではC言語における “文字列を数値に変換する方法” の解説を行いました!

主に atoi 系の関数と strtol 系の関数による変換方法を紹介をしましたが、これらの大きな違いは文字列に “不正な文字が含まれていることが判断可能かどうか” です。

とりあえずサクッと文字列を数値に変換したいのであれば atoi 系の関数を使えば良いですし、文字列に不正な文字が含まれていないかどうかをしっかり判断したいような場合は strtol 系の関数を使うのが良いと思います!

特にコマンド引数から受け取った文字列を数値として扱いたい場合に重宝しますので、atoi 系と strtol 系の関数があることはしっかり覚えておきましょう!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です