Cで文字列を整数に変換する方法は?


260

Cで文字列を整数に変換する別の方法があるかどうかを確認しようとしています。

私は定期的に次のコードをパターン化しています。

char s[] = "45";

int num = atoi(s);

それで、より良い方法または別の方法がありますか?


21
あなたのタグとタイトルはCでの解決策が欲しいと言っていますが、あなたの質問はCまたはC ++を言っています。あなたはどれが欲しいですか?
インシリコ2011年

1
@ヤン、その混乱のため申し訳ありません。Cを優先します。
user618677 2011

1
動作しますが、エラーを処理する方法がないため、これは推奨される方法ではありません。入力を100%信頼できる場合を除いて、これを運用コードで使用しないでください。
Uwe Geuder、2015年

1
「より良い」を定義し、なぜ別の方法が必要なのを明確に述べます。
ローンの侯爵

3
@EJP自分を向上させるためだけに。
user618677 2018

回答:


185

ありstrtolIMO優れています。また、私は好みを取り入れてstrtonumいますので、持っている場合は使用してください(ただし、移植性がないことを忘れないでください)。

long long
     strtonum(const char *nptr, long long minval, long long maxval,
     const char **errstr);

編集

また、に興味があるかもしれないstrtoumaxし、strtoimaxそのC99で標準機能です。たとえば、次のように言うことができます。

uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
    /* Could not convert. */

とにかく、近づかないでatoiください:

呼び出しatoi(str)は以下と同等です:

(int) strtol(str, (char **)NULL, 10)

ただし、エラーの処理が異なる場合があります。値を表現できない場合の動作は未定義です。


何を含める必要がありstrtonumますか?暗黙の宣言警告が
何度も

@ trideceth12使用可能なシステムでは、で宣言する必要があります#<stdlib.h>。ただし、標準のstrtoumax代替手段を使用できます。
cnicutar 2013年

4
この回答は、質問者の最初のコードよりも短く見えません。
Azurespot 2014

11
@NoniA。簡潔さは常に良いですが、正確さを犠牲にするわけではありません。
cnicutar 2014

6
安全ではないほど間違っている。atoi()は、入力が有効な場合に機能します。しかし、atoi( "cat")を実行するとどうなるでしょうか。strtol()は、値をlongとして表すことができない場合の動作を定義していますが、atoi()はそうではありません。
ダニエルB.

27

堅牢なC89 strtolベースのソリューション

と:

  • 定義されていない動作はありません(atoi家族と同じように)
  • より厳密な整数の定義strtol(たとえば、先頭の空白や末尾のゴミ文字がない)
  • エラーケースの分類(たとえば、ユーザーに役立つエラーメッセージを提供するため)
  • 「テストスイート」
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

typedef enum {
    STR2INT_SUCCESS,
    STR2INT_OVERFLOW,
    STR2INT_UNDERFLOW,
    STR2INT_INCONVERTIBLE
} str2int_errno;

/* Convert string s to int out.
 *
 * @param[out] out The converted int. Cannot be NULL.
 *
 * @param[in] s Input string to be converted.
 *
 *     The format is the same as strtol,
 *     except that the following are inconvertible:
 *
 *     - empty string
 *     - leading whitespace
 *     - any trailing characters that are not part of the number
 *
 *     Cannot be NULL.
 *
 * @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
 *
 * @return Indicates if the operation succeeded, or why it failed.
 */
str2int_errno str2int(int *out, char *s, int base) {
    char *end;
    if (s[0] == '\0' || isspace(s[0]))
        return STR2INT_INCONVERTIBLE;
    errno = 0;
    long l = strtol(s, &end, base);
    /* Both checks are needed because INT_MAX == LONG_MAX is possible. */
    if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
        return STR2INT_OVERFLOW;
    if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
        return STR2INT_UNDERFLOW;
    if (*end != '\0')
        return STR2INT_INCONVERTIBLE;
    *out = l;
    return STR2INT_SUCCESS;
}

int main(void) {
    int i;
    /* Lazy to calculate this size properly. */
    char s[256];

    /* Simple case. */
    assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
    assert(i == 11);

    /* Negative number . */
    assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
    assert(i == -11);

    /* Different base. */
    assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
    assert(i == 17);

    /* 0 */
    assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
    assert(i == 0);

    /* INT_MAX. */
    sprintf(s, "%d", INT_MAX);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MAX);

    /* INT_MIN. */
    sprintf(s, "%d", INT_MIN);
    assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
    assert(i == INT_MIN);

    /* Leading and trailing space. */
    assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);

    /* Trash characters. */
    assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
    assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);

    /* int overflow.
     *
     * `if` needed to avoid undefined behaviour
     * on `INT_MAX + 1` if INT_MAX == LONG_MAX.
     */
    if (INT_MAX < LONG_MAX) {
        sprintf(s, "%ld", (long int)INT_MAX + 1L);
        assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
    }

    /* int underflow */
    if (LONG_MIN < INT_MIN) {
        sprintf(s, "%ld", (long int)INT_MIN - 1L);
        assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
    }

    /* long overflow */
    sprintf(s, "%ld0", LONG_MAX);
    assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);

    /* long underflow */
    sprintf(s, "%ld0", LONG_MIN);
    assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);

    return EXIT_SUCCESS;
}

GitHubアップストリーム

ベース:https : //stackoverflow.com/a/6154614/895245


3
ニースロバストstr2int()。Pedantic:を使用しますisspace((unsigned char) s[0])
chux-2016年

@chuxありがとう!(unsigned char)キャストが違いを生む理由をもう少し説明できますか?
Ciro Santilli郝海东冠状病六四事件法轮功

IAR Cコンパイラは警告していることl > INT_MAXと、l < INT_MINどちらかの結果は常に偽であるため、無意味な整数比較です。それらをに変更して警告をクリアするl >= INT_MAXl <= INT_MINどうなりますか?ARM Cで、長いint型の32ビット署名されているARM CおよびC ++での基本データ型
ecle

コードを変更@ecle l >= INT_MAX例返す:招く誤った機能をSTR2INT_OVERFLOW入力と"32767"し、16ビットint。条件付きコンパイルを使用します。
chux-モニカを2017年

if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;良いだろうif (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}使用にコードを呼び出すできるようにするerrnoにはint、範囲外。も同じですif (l < INT_MIN...
chux-モニカを復活させる'22

24

ato...グループの関数を使用しないでください。これらは壊れており、事実上役に立たない。sscanfどちらかといえば完璧ではありませんが、適度に良い解決策はを使用することです。

文字列を整数に変換するには、strto...グループの関数を使用する必要があります。あなたの特定のケースではそれはstrtol機能です。


7
sscanf型の範囲外の数値を変換しようとすると、実際には未定義の動作になります(たとえば、sscanf("999999999999999999999", "%d", &n))。
キーストンプソン、

1
@キーストンプソン:それはまさに私が言っていることです。atoi意味のある成功/失敗のフィードバックを提供せず、オーバーフローに対する未定義の動作があります。sscanfソートの成功/失敗のフィードバック(「適度に良くなる」戻り値)を提供しますが、オーバーフロー時の動作は未定義のままです。唯一strtolの実行可能なソリューションです。
AnT

1
同意した 私は、の潜在的に致命的な問題を強調したかっただけsscanfです。(私は時々使用することを告白しますがatoi、通常、ソースを削除する前に10分以上存続することを期待しないプログラムに使用します。)
Keith Thompson

5

少しatoi()をコーディングしてお楽しみください。

int my_getnbr(char *str)
{
  int result;
  int puiss;

  result = 0;
  puiss = 1;
  while (('-' == (*str)) || ((*str) == '+'))
  {
      if (*str == '-')
        puiss = puiss * -1;
      str++;
  }
  while ((*str >= '0') && (*str <= '9'))
  {
      result = (result * 10) + ((*str) - '0');
      str++;
  }
  return (result * puiss);
}

また、3行で古い再帰的にすることもできます=)


どうもありがとう..しかし、以下のコードがどのように機能するか教えていただけますか?code((* str)-'0')code
user618677

文字にはASCII値があります。Linuxを使用していない場合は、シェルでman asciiと入力するか、そうでない場合はtable-ascii.comにアクセスしてください。intの文字「0」= 68(と思う)が表示されます。したがって、「9」の数(「0」+ 9)を取得するには、9 =「9」-「0」を取得します。わかった?
jDourlens 2011

1
1)コードが許可する"----1" 2)int結果がであるはずの場合、オーバーフローを伴う未定義の動作がありますINT_MIN。考えてみましょうmy_getnbr("-2147483648")
chux -復活モニカ

正確にありがとう、それはほんの少しの例を示すためでした。それは楽しさと学習のためにそれを言ったように。この種のタスクには、明確にstandart libを使用する必要があります。より速く、より安全に!
jDourlens 2016年

2

署名されていない長い解決策も共有したかっただけです。

unsigned long ToUInt(char* str)
{
    unsigned long mult = 1;
    unsigned long re = 0;
    int len = strlen(str);
    for(int i = len -1 ; i >= 0 ; i--)
    {
        re = re + ((int)str[i] -48)*mult;
        mult = mult*10;
    }
    return re;
}

1
オーバーフローを処理しません。また、パラメータはである必要がありますconst char *
Roland Illig 2017年

2
さらに、それは48どういう意味ですか?それが'0'コードが実行される場所の価値であると想定していますか?世界にそのような広範な仮定を与えないでください!
Toby Speight

@TobySpeightはい、48はASCIIテーブルで「0」を表すと想定しています。
ジェイコブ

3
すべての世界がASCIIであるわけではありません- '0'必要に応じて使用してください。
Toby Speight

代わりにstrtoul関数を使用することをお勧めします。
Rapidclock

1
int atoi(const char* str){
    int num = 0;
    int i = 0;
    bool isNegetive = false;
    if(str[i] == '-'){
        isNegetive = true;
        i++;
    }
    while (str[i] && (str[i] >= '0' && str[i] <= '9')){
        num = num * 10 + (str[i] - '0');
        i++;
    }
    if(isNegetive) num = -1 * num;
    return num;
}

-1

あなたはいつでもあなた自身のものを転がすことができます!

#include <stdio.h>
#include <string.h>
#include <math.h>

int my_atoi(const char* snum)
{
    int idx, strIdx = 0, accum = 0, numIsNeg = 0;
    const unsigned int NUMLEN = (int)strlen(snum);

    /* Check if negative number and flag it. */
    if(snum[0] == 0x2d)
        numIsNeg = 1;

    for(idx = NUMLEN - 1; idx >= 0; idx--)
    {
        /* Only process numbers from 0 through 9. */
        if(snum[strIdx] >= 0x30 && snum[strIdx] <= 0x39)
            accum += (snum[strIdx] - 0x30) * pow(10, idx);

        strIdx++;
    }

    /* Check flag to see if originally passed -ve number and convert result if so. */
    if(!numIsNeg)
        return accum;
    else
        return accum * -1;
}

int main()
{
    /* Tests... */
    printf("Returned number is: %d\n", my_atoi("34574"));
    printf("Returned number is: %d\n", my_atoi("-23"));

    return 0;
}

これにより、ごちゃごちゃすることなく必要な操作を実行できます。


2
しかし、なぜ?これはオーバーフローをチェックせず、単にガベージ値を無視します。strto...関数ファミリーを使用しない理由はありません。彼らは移植性があり、大幅に優れています。
2015

1
0x2d, 0x30代わりに使用するのは奇妙です'-', '0''+'サインを許可しません。なぜ(int)キャストイン(int)strlen(snum)?入力がの場合はUB ""。結果がオーバーフローINT_MINによるものである場合のUBintaccum += (snum[strIdx] - 0x30) * pow(10, idx);
chux-モニカの復活2016年

@chux-このコードはデモコードです。潜在的な問題としてあなたが説明したものに簡単な修正があります。
ButchDean

2
@ButchDean「デモンストレーションコード」としてあなたが説明するものは、すべての詳細について何の手掛かりもない他の人によって使用されます。否定的なスコアとこの回答のコメントのみが、それらを保護します。私の意見では、「デモンストレーションコード」ははるかに高品質でなければなりません。
Roland Illig 2017年

@RolandIlligすべてが重要というよりは、実際に独自のソリューションを提供する方が他の人にとってより役立つのではないでしょうか。
ButchDean 2017年

-1

この機能はあなたを助けます

int strtoint_n(char* str, int n)
{
    int sign = 1;
    int place = 1;
    int ret = 0;

    int i;
    for (i = n-1; i >= 0; i--, place *= 10)
    {
        int c = str[i];
        switch (c)
        {
            case '-':
                if (i == 0) sign = -1;
                else return -1;
                break;
            default:
                if (c >= '0' && c <= '9')   ret += (c - '0') * place;
                else return -1;
        }
    }

    return sign * ret;
}

int strtoint(char* str)
{
    char* temp = str;
    int n = 0;
    while (*temp != '\0')
    {
        n++;
        temp++;
    }
    return strtoint_n(str, n);
}

参照:http : //amscata.blogspot.com/2013/09/strnumstr-version-2.html


1
なぜこれを行うのですか?atoiおよび友達の最大の問題の1つは、オーバーフローが発生した場合の動作が未定義であることです。関数はこれをチェックしません。strtolと友達が行います。
2015

1
うん。CはPythonではないので、C言語を使用する人々がこの種のオーバーフローエラーを認識していることを願っています。すべてに独自の限界があります。
Amith Chinthaka

-1

わかりました、私は同じ問題を抱えていました。私はこの解決策を思いつきました。それは私にとって最もうまくいきました.atoi()を試してみましたが、私にとってはうまくいきませんでした。

void splitInput(int arr[], int sizeArr, char num[])
{
    for(int i = 0; i < sizeArr; i++)
        // We are subtracting 48 because the numbers in ASCII starts at 48.
        arr[i] = (int)num[i] - 48;
}

-1
//I think this way we could go :
int my_atoi(const char* snum)
{
 int nInt(0);
 int index(0);
 while(snum[index])
 {
    if(!nInt)
        nInt= ( (int) snum[index]) - 48;
    else
    {
        nInt = (nInt *= 10) + ((int) snum[index] - 48);
    }
    index++;
 }
 return(nInt);
}

int main()
{
    printf("Returned number is: %d\n", my_atoi("676987"));
    return 0;
}

コードがCでコンパイルされない。なぜnInt = (nInt *= 10) + ((int) snum[index] - 48);vs. nInt = nInt*10 + snum[index] - '0'; if(!nInt)必要ないのか。
chux-2016年

-3

C ++では、次のような関数を使用できます。

template <typename T>
T to(const std::string & s)
{
    std::istringstream stm(s);
    T result;
    stm >> result;

    if(stm.tellg() != s.size())
        throw error;

    return result;
}

これは、任意の文字列をfloat、int、doubleなどの任意の型に変換するのに役立ちます...


1
C ++をカバーする同様の質問が既にあり、このアプローチの問題が説明されています。
Ben Voigt

-6

はい、整数を直接保存できます:

int num = 45;

文字列を解析する必要がある場合、atoiまたはstrol「最短のコード」コンテストに勝つ場合。


安全に実行したい場合は、strtol()実際にはかなりの量のコードが必要です。これは、返すことができるLONG_MINLONG_MAX のどちらかそれは実際の変換された値だ場合、またはそこにアンダーフローまたはオーバーフローだし、それが実際の値だならば、それはどちらか0を返すことができるか、変換するために、何の番号がなかった場合場合。errno = 0通話の前に設定し、を確認する必要がありendptrます。
キーストンプソン

解析に与えられたソリューションは、実行可能なソリューションではありません。
BananaAcid
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.