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);

편집하다

당신은 또한에 관심이있을 수 strtoumaxstrtoimax 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_MAXl <= INT_MIN경고를 취소? ARM C에서 longintARM 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;}호출 코드가 범위 errnoint벗어난 상태 에서 사용할 수 있도록하는 것이 좋습니다 . 동일합니다 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.