C에서 문자열을 정수로 변환하는 방법은 무엇입니까?


260

C에서 문자열을 정수로 변환하는 다른 방법이 있는지 확인하려고합니다.

나는 정기적으로 내 코드에서 다음을 패턴 화합니다.

char s[] = "45";

int num = atoi(s);

더 나은 방법이 있습니까? 아니면 다른 방법이 있습니까?


21
태그와 제목에 C로 솔루션을 원한다고 말했지만 C 또는 C ++로 질문합니다. 어느 쪽을 원하십니까?
— silico에서

1
@Yann, 혼란을 드려 죄송합니다. 나는 C를 선호 할 것이다.
— user618677

1
작동하지만 오류를 처리 할 수있는 방법이 없기 때문에 권장되는 방법은 아닙니다. 입력을 100 % 신뢰할 수 없다면 프로덕션 코드에서는 이것을 사용하지 마십시오.
— Uwe Geuder

1
'더 나은'을 정의하고 왜 다른 방법이 필요한지 명확하게 설명 하십시오.
— Lorne의 후작

3
@EJP 단지 나 자신을 향상시키기 위해.
— user618677

답변:


185

이 strtol더 나은 IMO이다. 또한 나는 마음에 들었 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합니까? 암시 적 선언 경고가 계속 표시됩니다.
— jsj

@ trideceth12 사용 가능한 시스템에서는로 선언해야합니다 #<stdlib.h>. 그러나 표준 strtoumax대안을 사용할 수 있습니다 .
— cnicutar

4
이 답변은 질문자의 첫 번째 코드보다 짧지 않습니다.
— Azurespot

11
@NoniA. 간결함은 항상 좋지만 정확성을 희생하지는 않습니다.
— cnicutar

6
안전하지 않은 것만 큼 잘못은 아닙니다. 입력이 유효하면 atoi ()가 작동합니다. 그러나 atoi ( "cat")를하면 어떻게 될까요? 값을 long으로 표시 할 수 없으면 attol ()에서는 strtol ()이 동작을 정의한 것입니다.
— Daniel 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-복원 Monica Monica

@chux 감사합니다! (unsigned char)캐스트가 차이를 만들 수있는 이유를 좀 더 설명해 주 시겠습니까?
— Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

IAR C 컴파일러는 있다고 경고 l > INT_MAX하고 l < INT_MIN하나 결과는 항상 false이기 때문에 의미가 정수 비교입니다. 내가으로 변경하면 어떻게됩니까 l >= INT_MAX과 l <= INT_MIN경고를 취소? ARM C에서 long 및 int 는 ARM C 및 C ++의
— ecle

@ecle 코드를 변경하여 l >= INT_MAX잘못된 기능 이 발생합니다 (예 : STR2INT_OVERFLOWinput "32767"및 16-bit 반환) int. 조건부 컴파일을 사용하십시오. 예 .
— chux-Reinstate Monica

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-복원 모니카

24

ato...그룹의 기능을 사용하지 마십시오 . 이것들은 깨졌으며 사실상 쓸모가 없습니다. sscanf완벽하지는 않지만 적당히 더 나은 솔루션을 사용하는 것이 좋습니다 .

문자열을 정수로 변환하려면 strto...그룹의 함수를 사용해야합니다. 특정 경우에는 strtol기능입니다.


7
sscanf실제로 유형 범위 밖의 숫자를 변환하려고하면 실제로 정의되지 않은 동작이 있습니다 (예 sscanf("999999999999999999999", "%d", &n):).
— Keith Thompson

1
@Keith Thompson : 그것이 바로 제가 의미하는 바입니다. 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 값을 갖습니다. 당신이 Uner에 리눅스 유형 인 경우 : 쉘에서 남자 ASCII 또는 그렇지 않은 경우로 이동 table-ascii.com . int에 대한 문자 '0'= 68 (생각합니다)을 알 수 있습니다. 따라서 '9'( '0'+ 9)의 수를 얻으려면 9 = '9'- '0'을 얻습니다. 알 겠어요?
— jDourlens

1
1) 코드에서 허용 "----1" 2) int결과가이어야 할 때 오버플로로 정의되지 않은 동작 이 있습니다 INT_MIN. 고려my_getnbr("-2147483648")
— chux-복원 Monica Monica

정확성에 감사드립니다. 단지 작은 예를 보여주기위한 것이 었습니다. 그것은 재미와 학습을 위해 말했듯이. 이런 종류의 작업에는 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 2012 년

2
게다가 그게 무슨 48뜻입니까? 그것이 '0'코드가 실행될 곳의 가치라고 가정 합니까? 세상에 그런 광범위한 가정을 가하지 마십시오!
— Toby Speight

@TobySpeight 예 ascii 테이블에서 48이 '0'이라고 가정합니다.
— Jacob

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...기능 군 을 사용하지 않는 이유는 없습니다 . 휴대 성이 뛰어나고 훨씬 뛰어납니다.
— 차드

1
0x2d, 0x30대신에 사용하는 것이 이상합니다 '-', '0'. '+'부호를 허용하지 않습니다 . 왜 (int)캐스트 (int)strlen(snum)? UB 입력되면 "". 결과가 오버플로 INT_MIN로 인한 경우 UBintaccum += (snum[strIdx] - 0x30) * pow(10, idx);
— chux-Reinstate Monica

@chux-이 코드는 데모 코드입니다. 잠재적 인 문제로 설명 된 내용을 쉽게 수정할 수 있습니다.
— ButchDean

2
@ButchDean "데모 코드"라고 설명하는 것은 모든 세부 사항에 대한 단서가없는 다른 사람들이 사용합니다. 부정적인 답변과이 답변에 대한 의견 만이 지금 그들을 보호합니다. 제 생각에는 "데모 코드"는 훨씬 더 높은 품질을 가져야합니다.
— Roland Illig

@RolandIllig 모든 비판적이기보다는 다른 사람들이 실제로 자신의 솔루션을 만드는 것이 더 도움이되지 않습니까?
— ButchDean

-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친구 와의 가장 큰 문제 중 하나는 오버플로가 발생하면 정의되지 않은 동작이라는 것입니다. 함수가이를 확인하지 않습니다. strtol친구들은
— 차드

1
예. C는 파이썬이 아니기 때문에 C 언어를 사용하는 사람들이 이러한 종류의 오버플로 오류를 알고 있기를 바랍니다. 모든 것은 자신의 한계가 있습니다.
— 아 미스 친 타카

-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);대이 nInt = nInt*10 + snum[index] - '0'; if(!nInt)필요하지 않습니다.
— chux-복원 Monica Monica

-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 ++에 대한 비슷한 질문 이 이미 있습니다 .
— 벤 보이 그

-6

예, 정수를 직접 저장할 수 있습니다.

int num = 45;

문자열을 구문 분석해야 atoi하거나 strol"최단 코드 양"콘테스트에서 우승하려는 경우.


안전하게 수행 strtol()하려면 실제로 상당한 양의 코드가 필요합니다. 그것은 반환 할 수 있습니다 LONG_MIN또는 LONG_MAX 하나 그 실제 변환 된 값의 경우, 또는이 언더 플로우 또는 오버 플로우, 그리고 그 실제 값이 있다면이 중 하나를 0을 반환 할 수 있습니다 또는 변환에는 수 없었다 경우합니다. errno = 0통화하기 전에 설정 하고을 확인해야합니다 endptr.
— Keith Thompson

파싱하기 위해 제공된 솔루션은 실행 가능한 솔루션이 아닙니다.
— BananaAcid
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.