C ++에서 문자열을 정수로 구문 분석하는 방법은 무엇입니까?


261

문자열을 char로 파싱하는 C ++ 방법은 무엇입니까? 강력하고 명확한 오류 처리는 0반환하는 대신 플러스 입니다.


21
codeproject.com/KB/recipes/Tokenizer.aspx 다음 중 일부 예는 어떻습니까? 매우 효율적이고 다소 우아합니다.

@Beh Tou Cheh, int를 파싱하는 것이 좋은 방법이라고 생각되면 답변으로 게시하십시오.
유진 요코타

답변:


165

새로운 C ++ 11에는 stoi, stol, stoll, stoul 등의 기능이 있습니다.

int myNr = std::stoi(myString);

변환 오류에 대한 예외가 발생합니다.

이 새로운 함수조차도 Dan이 언급 한 것과 같은 문제가 있습니다. 그들은 문자열 "11x"를 정수 "11"로 행복하게 변환합니다.

더보기 : http://en.cppreference.com/w/cpp/string/basic_string/stol


4
그러나 그들은 그 중 하나의가없는 경우는 null, 최초의 변환되지 않은 캐릭터로 설정되어 있는지를 size_t에 포인트되는,보다 인수를 사용할
Zharf

예, std :: stoi의 두 번째 매개 변수를 사용하면 유효하지 않은 입력을 감지 할 수 있습니다. 그래도 여전히 자신 만의 변환 기능을 실행해야합니다.
CC.

받아 들여진 대답과 마찬가지로 훨씬 더 깔끔한 표준 기능을 사용하면 imo
Zharf 님이

이 함수의 두 번째 인수는 전체 문자열이 변환되었는지 여부를 나타내는 데 사용될 수 있습니다. 결과 size_t가 문자열의 길이와 같지 않으면 일찍 중지되었습니다. 이 경우 여전히 11을 반환하지만 pos문자열 길이 3 대신 2가됩니다. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Zoe

204

하지 말아야 할 것

여기에 첫 번째 조언이 있습니다 : 이것을 위해 stringstream을 사용하지 마십시오 . 처음에는 사용하기가 간단 해 보이지만 견고성과 오류 처리 기능을 원하면 추가 작업을 많이 수행해야합니다.

직관적으로 작동하는 것처럼 보이는 접근법이 있습니다.

bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

이것은 큰 문제가 있습니다 str2int(i, "1337h4x0r")행복하게 리턴을 true하고 i값을 얻을 것이다 1337. stringstream변환 후 더 이상 문자가 없도록하여이 문제를 해결할 수 있습니다 .

bool str2int (int &i, char const *s)
{
    char              c;
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail() || ss.get(c)) {
        // not an integer
        return false;
    }
    return true;
}

한 가지 문제를 해결했지만 여전히 몇 가지 다른 문제가 있습니다.

문자열의 숫자가 10이 아닌 경우 어떻게합니까? ss << std::hex변환을 시도하기 전에 스트림을 올바른 모드 (예 :)로 설정하여 다른베이스를 수용 할 수 있습니다 . 그러나이 방법은 호출자는 알고 있어야합니다 선험적 수입니다 기반으로 무엇을 - 어떻게 호출이 가능한 것을 알 수 있습니까? 발신자는 아직 번호가 무엇인지 모릅니다. 그들은 심지어는 모르겠어요 입니다숫자! 그들은 그것이 어떤 기반인지 어떻게 알 수 있습니까? 프로그램에 입력 한 모든 숫자는 10 진수 여야하며 16 진수 또는 8 진수 입력을 유효하지 않은 것으로 거부 할 수 있습니다. 그러나 그것은 매우 유연하거나 강력하지 않습니다. 이 문제에 대한 간단한 해결책은 없습니다. 10 진수 변환은 항상 8 진수 (앞의 0)에 성공하고 8 진수 변환은 일부 10 진수에 성공할 수 있기 때문에 각 밑수에 대해 한 번만 변환을 시도 할 수 없습니다. 이제 앞에 0을 확인해야합니다. 하지만 기다려! 16 진 숫자는 선행 0으로 시작할 수도 있습니다 (0x ...). 한숨.

위의 문제를 성공적으로 처리하더라도 더 큰 문제가 있습니다. 호출자가 잘못된 입력 (예 : "123foo")과 범위를 벗어난 숫자 int(예 : "4000000000" )를 구분해야하는 경우 32 비트 int)? 을 사용하면 stringstream이 구분을 할 수있는 방법이 없습니다. 전환 성공 여부 만 알 수 있습니다. 실패하면 실패한 이유 를 알 수 없습니다 . 보시다시피 stringstream견고성과 명확한 오류 처리를 원한다면 많은 것을 원합니다.

이것은 나를 두 번째 조언으로 인도합니다 : 이것을 위해 Boost 's lexical_cast를 사용하지 마십시오 . lexical_cast문서가 말한 것을 고려하십시오 .

변환에 대해 더 높은 수준의 제어가 필요한 경우 std :: stringstream 및 std :: wstringstream은보다 적절한 경로를 제공합니다. 비 스트림 기반 변환이 필요한 경우 lexical_cast는 작업에 대한 잘못된 도구이며 이러한 시나리오에는 적합하지 않습니다.

뭐?? 우리는 이미 stringstream제어 수준이 좋지 않은 것을 보았지만 "높은 수준의 제어"가 필요한 경우 stringstream대신 사용해야합니다 lexical_cast. 또한 lexical_cast단지 래퍼 이기 때문에 stringstream동일한 수의 문제를 겪 stringstream습니다. 여러 개의 숫자 기반에 대한 지원이 잘못되고 오류 처리가 잘못되었습니다.

최고의 솔루션

다행히도 누군가 위의 모든 문제를 이미 해결했습니다. C 표준 라이브러리에는 strtol이러한 문제가없는 패밀리가 포함되어 있습니다.

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
        return OVERFLOW;
    }
    if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
        return UNDERFLOW;
    }
    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }
    i = l;
    return SUCCESS;
}

모든 오류 사례를 처리하고 2에서 36까지의 숫자를 지원하는 무언가에 대해 매우 간단합니다. base0 (기본값)이면 모든 밑에서 변환을 시도합니다. 또는 호출자가 세 번째 인수를 제공하고 특정 기준에 대해서만 변환을 시도하도록 지정할 수 있습니다. 강력하고 최소한의 노력으로 모든 오류를 처리합니다.

선호해야하는 다른 이유 strtol(및 가족) :

  • 런타임 성능 이 훨씬 뛰어납니다.
  • 컴파일 타임 오버 헤드가 적습니다 (다른 것들은 헤더에서 거의 20 배 더 많은 SLOC를 가져옵니다)
  • 가장 작은 코드 크기

다른 방법을 사용해야 할 이유는 없습니다.


22
@JamesDunne : POSIX는 strtol스레드로부터 안전해야합니다. POSIX는 또한 errno스레드 로컬 스토리지를 사용해야합니다. 비 POSIX 시스템에서도 errno멀티 스레드 시스템 의 거의 모든 구현은 스레드 로컬 스토리지를 사용합니다. 최신 C ++ 표준은 errnoPOSIX를 준수해야합니다. 최신 C 표준 errno에는 스레드 로컬 저장소가 있어야 합니다. 확실히 POSIX와 호환되지 않는 Windows에서도 errno스레드로부터 안전하며 확장 적으로도 마찬가지입니다 strtol.
Dan Molding

7
boost :: lexical_cast를 사용하는 것에 대한 당신의 추론을 실제로 따를 수는 없습니다. 그들이 말했듯이 std :: stringstream은 실제로 많은 제어 기능을 제공합니다. 오류 확인에서 기본 판단에 이르기까지 모든 것을 수행합니다. 현재 문서는 다음과 같이 설명했다. "정밀도 또는 형식이 lexical_cast의 기본 동작보다 더 엄격한 제어를 필요로하는 경우와 같이보다 복잡한 변환을 위해서는 기존의 std :: stringstream 접근 방식이 권장됩니다."
fhd

8
이것은 C ++ 내에서 부적절한 C 코딩입니다. 이를 위해 표준 라이브러리에 std::stol상수를 반환하는 대신 예외를 적절하게 발생시킵니다.
fuzzy15 년

22
@fuzzyTew 나는이 답변을 std::stolC ++ 언어에 추가 하기 전에 썼습니다 . 즉, 이것이 "C ++ 내의 C 코딩"이라고 말하는 것이 공정하지 않다고 생각합니다. std::strtolC ++ 언어의 일부인 경우 C 코딩 이라고하는 것은 어리석은 일입니다. 내 대답은 작성되었을 때 C ++에 완벽하게 적용되었으며 새로운에서도 적용됩니다 std::stol. 예외가 발생할 수있는 함수를 호출하는 것이 모든 프로그래밍 상황에 항상 적합한 것은 아닙니다.
Dan Molding

9
@fuzzyTew : 디스크 공간이 부족한 경우는 예외입니다. 컴퓨터로 생성 된 형식이 잘못된 데이터 파일은 예외적 인 조건입니다. 그러나 사용자 입력 오타는 예외가 아닙니다. 일반적인 예외가 아닌 파싱 오류를 처리 할 수있는 파싱 방법을 사용하는 것이 좋습니다.
Ben Voigt

67

이것은 atoi ()보다 안전한 C 방식입니다.

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

표준 라이브러리 문자열 스트림이있는 C ++ : (감사합니다 CMS )

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

부스트 라이브러리 (감사 JK )

#include <boost/lexical_cast.hpp>
#include <string>

try
{
    std::string str = "123";
    int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
    // Error
}

편집 : 오류를 처리하도록 문자열 스트림 버전을 수정했습니다. (원래 게시물에 대한 CMS 및 jk의 의견 덕분에)


1
stringstream :: fail ()에 대한 검사를 포함하도록 stringstream 버전을 업데이트하십시오 (질문자 "견고하고 명확한 오류 처리"에 의해 요청 됨)
jk.

2
stringstream 버전은 불평하지 않고 "10haha"와 같은 항목을 허용합니다
Johannes Schaub-litb

3
lexical_cast와 같은 처리를 원한다면 (! (ss >> num) .fail () && (ss >> ws) .eof ())에서 ((ss >> num) .fail ())로 변경하십시오
Johannes Schaub-litb

3
표준 라이브러리 stringstream 메소드를 사용하는 C ++은 .fail () 검사를 사용해도 "12-SomeString"과 같은 문자열에 대해 작동하지 않습니다.
captonssj 2009

C ++ 11은 지금이 표준 빠른 기능을 포함
fuzzyTew

21

좋은 '구 C 방식은 여전히 ​​작동합니다. 나는 strtol 또는 strtoul을 추천합니다. 리턴 상태와 'endPtr'사이에서 우수한 진단 출력을 제공 할 수 있습니다. 또한 여러 기지를 잘 처리합니다.


4
C ++를 프로그래밍 할 때이 오래된 C를 사용하지 마십시오. C ++ 에서이 작업을 수행하는 것이 더 쉽고 더 쉽고 깨끗하며 현대적이고 안전합니다.
jk.

27
사람들이 문제를 해결하는 "더 현대적인"방법에 대해 걱정할 때 재미 있습니다.
J Miller

@Jason, IMO 강력한 유형 안전 및 오류 처리는 C에 비해 더 현대적인 아이디어입니다.
Eugene Yokota

6
나는 다른 답변을 보았으며, 지금까지 더 나은 / 쉬운 / 깨끗한 또는 더 안전한 것은 없습니다. 포스터는 그가 숯불을 가지고 있다고 말했다. 즉 :) 안전의 양이 당신이 얻을거야 제한
크리스 Arguin에게


16

C ++ 표준 libraray의 문자열 스트림을 사용할 수 있습니다.

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

정수를 읽으려고 할 때 숫자가 아닌 경우 스트림 상태는 실패하도록 설정됩니다.

C ++의 오류 처리 및 스트림 함정에 대해서는 스트림 함정 을 참조하십시오 .


2
C ++ stringstream 메소드는 'stream state'확인을 사용하더라도 "12-SomeString"과 같은 문자열에 대해 작동하지 않습니다.
captonssj 2009

10

stringstream 을 사용할 수 있습니다

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}

4
그러나 이것은 오류를 처리하지 않습니다. 스트림에 장애가 있는지 확인해야합니다.
jk.

1
바로 스트림을 확인해야합니다 if ((ss >> num) .fail ()) {// ERROR}
CMS

2
'스트림 상태'확인을 사용해도 "12-SomeString"과 같은 문자열에는 C ++ 문자열 스트림 방법이 작동하지 않습니다.
captonssj

8

이 세 가지 링크가 요약되어 있다고 생각합니다.

stringstream 및 lexical_cast 솔루션은 어휘 캐스트가 stringstream을 사용하는 것과 거의 같습니다.

어휘 캐스트의 일부 특수화는 다른 접근법을 사용 합니다. 자세한 내용 은 http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp 를 참조하십시오. 정수 및 부동 소수점은 이제 정수에서 문자열로의 변환에 특화되었습니다.

lexical_cast를 자신의 필요에 맞게 전문화하고 빠르게 만들 수 있습니다. 이것은 깨끗하고 간단한 모든 당사자를 만족시키는 최고의 솔루션입니다.

이미 언급 한 기사는 정수 <-> 문자열을 변환하는 다른 방법 간의 비교를 보여줍니다. 구식 c-way, spirit.karma, fastformat, simple naive loop 등의 접근 방식이 적합합니다.

예를 들어 int에서 문자열로 변환하는 경우 Lexical_cast가 정상입니다.

어휘 캐스트를 사용하여 문자열을 int로 변환하는 것은 사용되는 플랫폼 / 컴파일러에 따라 atoi보다 10-40 배 느리므로 좋지 않습니다.

Boost.Spirit.Karma는 정수를 문자열로 변환하는 가장 빠른 라이브러리 인 것 같습니다.

ex.: generate(ptr_char, int_, integer_number);

위에서 언급 한 기사의 기본 간단한 루프는 문자열을 int로 변환하는 가장 빠른 방법이며, 가장 안전한 것은 아닙니다. strtol ()은 더 안전한 솔루션처럼 보입니다.

int naive_char_2_int(const char *p) {
    int x = 0;
    bool neg = false;
    if (*p == '-') {
        neg = true;
        ++p;
    }
    while (*p >= '0' && *p <= '9') {
        x = (x*10) + (*p - '0');
        ++p;
    }
    if (neg) {
        x = -x;
    }
    return x;
}

7

C ++ 문자열 툴킷 라이브러리 (StrTk는) 다음과 같은 솔루션을 제공합니다 :

static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
   0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
   0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 0xF8 - 0xFF
 };

template<typename InputIterator, typename T>
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
   if (0 == std::distance(begin,end))
      return false;
   v = 0;
   InputIterator it = begin;
   bool negative = false;
   if ('+' == *it)
      ++it;
   else if ('-' == *it)
   {
      ++it;
      negative = true;
   }
   if (end == it)
      return false;
   while(end != it)
   {
      const T digit = static_cast<T>(digit_table[static_cast<unsigned int>(*it++)]);
      if (0xFF == digit)
         return false;
      v = (10 * v) + digit;
   }
   if (negative)
      v *= -1;
   return true;
}

InputIterator는 부호없는 char *, char * 또는 std :: string 반복자 일 수 있으며 T는 signed int, int 또는 long과 같은 signed int 일 것으로 예상됩니다.


1
경고이 구현은 멋지지만 내가 알 수있는 한 오버플로를 처리하지 않습니다.
Vinnie Falco

2
코드가 오버플로를 처리하지 않습니다. v = (10 * v) + digit;텍스트 값이 인 문자열 입력으로 불필요하게 오버플로 INT_MIN됩니다. 테이블은 의심스러운 가치 대 단순히digit >= '0' && digit <= '9'
chux-Reinstate Monica

6

당신이 C ++ (11)이있는 경우, 해당 솔루션은 현재의 변환 기능 정수 C ++이다 <string>: stoi, stol, stoul, stoll, stoull. 입력이 잘못되면 적절한 예외 strto*를 처리하고 후드 아래에서 빠르고 작은 기능을 사용합니다 .

C ++의 이전 버전이 고착 된 경우 구현에서 이러한 기능을 모방하는 것이 가능합니다.


6

C ++ 17부터 여기에 설명 된대로 헤더 std::from_chars에서 사용할 수 있습니다 .<charconv>

예를 들면 다음과 같습니다.

#include <iostream>
#include <charconv>
#include <array>

int main()
{
    char const * str = "42";
    int value = 0;

    std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);

    if(result.error == std::errc::invalid_argument)
    {
      std::cout << "Error, invalid format";
    }
    else if(result.error == std::errc::result_out_of_range)
    {
      std::cout << "Error, value too big for int range";
    }
    else
    {
      std::cout << "Success: " << result;
    }
}

보너스로 16 진수와 같은 다른 기반도 처리 할 수 ​​있습니다.


3

나는 Dan Moulding의 대답이 마음에 들며 약간의 C ++ 스타일을 추가 할 것입니다.

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

int to_int(const std::string &s, int base = 0)
{
    char *end;
    errno = 0;
    long result = std::strtol(s.c_str(), &end, base);
    if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
        throw std::out_of_range("toint: string is out of range");
    if (s.length() == 0 || *end != '\0')
        throw std::invalid_argument("toint: invalid string");
    return result;
}

암시 적 변환을 통해 std :: string 및 const char * 모두에서 작동합니다. 또한 기본 변환에 유용합니다 (예 : all to_int("0x7b")and to_int("0173")and to_int("01111011", 2)and to_int("0000007B", 16)and to_int("11120", 3)and to_int("3L", 34);123).

달리 std::stoi이 전 C ++ 11에서 작동합니다. 또한 달리 std::stoi, boost::lexical_caststringstream 그것은 "123hohoho"와 같은 이상한 문자열에 대한 예외가 발생합니다.

주의 :이 기능은 공간을 선도하고 관대하지만, 후행 공백이 아닌, 즉 to_int(" 123")123 동안을 반환to_int("123 ") 예외 던지는 . 이것이 사용 사례에 적합한 지 확인하거나 코드를 조정하십시오.

이러한 기능은 STL의 일부가 될 수 있습니다 ...


2

String을 int로 변환하는 세 가지 방법을 알고 있습니다.

stoi (String to int) 함수를 사용하거나 개별 변환을 수행하는 세 번째 방법 인 Stringstream과 함께 사용하면 Code는 다음과 같습니다.

첫 번째 방법

std::string s1 = "4533";
std::string s2 = "3.010101";
std::string s3 = "31337 with some string";

int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);

std::cout <<  s1 <<"=" << myint1 << '\n';
std::cout <<  s2 <<"=" << myint2 << '\n';
std::cout <<  s3 <<"=" << myint3 << '\n';

두 번째 방법

#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;


int StringToInteger(string NumberAsString)
{
    int NumberAsInteger;
    stringstream ss;
    ss << NumberAsString;
    ss >> NumberAsInteger;
    return NumberAsInteger;
}
int main()
{
    string NumberAsString;
    cin >> NumberAsString;
    cout << StringToInteger(NumberAsString) << endl;
    return 0;
} 

세 번째 방법-개별 전환에는 적용되지 않음

std::string str4 = "453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{

    in = str4[i];
    cout <<in-48 ;

}

1

나는 Dan의 대답을 좋아한다 예외를 피하기 때문에 합니다. 임베디드 시스템 개발 및 기타 저수준 시스템 개발의 경우 사용 가능한 적절한 예외 프레임 워크가 없을 수 있습니다.

유효한 문자열 다음에 공백 검사가 추가되었습니다 ...이 세 줄

    while (isspace(*end)) {
        end++;
    }


구문 분석 오류 검사도 추가했습니다.

    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }


다음은 완전한 기능입니다 ..

#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
    char *end = (char *)s;
    errno = 0;

    l = strtol(s, &end, base);

    if ((errno == ERANGE) && (l == LONG_MAX)) {
        return OVERFLOW;
    }
    if ((errno == ERANGE) && (l == LONG_MIN)) {
        return UNDERFLOW;
    }
    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }    
    while (isspace((unsigned char)*end)) {
        end++;
    }

    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }

    return SUCCESS;
}

@chux는 언급 한 문제를 해결하기 위해 코드를 추가했습니다.
pellucide

1) 여전히 같은 입력으로 오류를 감지하지 못합니다 " ". 변환이 발생하지 않을 때 strtol()설정하도록 지정되지 않았습니다 errno. if (s == end) return INCONVERTIBLE; 전환 없음을 감지하는 데 사용 하는 것이 좋습니다 . 그리고 다음 if (*s == '\0' || *end != '\0')에 간단하게 할 수 if (*end)2) || l > LONG_MAX|| l < LONG_MIN어떤 목적에 봉사하지 - 그들은 진정한 적이 없습니다.
chux-복원 Monica Monica

@chux Mac에서는 errno가 구문 분석 오류로 설정되었지만 Linux에서는 errno가 설정되지 않았습니다. 이를 감지하기 위해 "end"포인터에 의존하도록 코드를 변경했습니다.
pellucide

0

이 정의 된 방법을 사용할 수 있습니다.

#define toInt(x) {atoi(x.c_str())};

그리고 String에서 Integer로 변환하려면 다음을 수행하십시오.

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

출력은 102입니다.


4
idk. 정의 매크로를 작성하는 atoi것은 accept와 같은 다른 답변에 비추어 "C ++ 방식"처럼 보이지 않습니다 std::stoi().
유진 요코타

나는 미리 정의 된 방법을 사용하여 더 재미를 발견 : P
Boris

0

나는 이것이 오래된 질문이라는 것을 알고 있지만, 나는 그것을 여러 번 보았으며 현재까지 다음과 같은 특징을 가진 멋진 템플릿 솔루션을 찾지 못했습니다.

  • 모든 염기를 변환 할 수 있으며 염기 유형을 감지 할 수 있습니다
  • 잘못된 데이터를 감지합니다 (예 : 전체 문자열, 적은 선행 / 트레일 링 공백이 변환에 사용되도록 보장)
  • 변환 된 유형에 관계없이 문자열 값의 범위가 허용되는지 확인하십시오.

자, 여기 테스트 스트랩이 있습니다. 후드 아래에서 C 함수 strtoull / strtoll을 사용하기 때문에 항상 사용 가능한 가장 큰 유형으로 항상 변환됩니다. 그런 다음 가장 큰 유형을 사용하지 않는 경우 추가 범위 확인을 수행하여 유형이 초과 (미달)되지 않았는지 확인합니다. 이를 위해 strtol / strtoul을 올바르게 선택한 경우보다 성능이 약간 떨어집니다. 그러나 그것은 shorts / chars에도 작동하며, 내가 아는 한, 표준 라이브러리 기능도 없습니다.

즐겨; 잘하면 누군가가 유용하다고 생각합니다.

#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>

static const int DefaultBase = 10;

template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
    while (isspace(*str)) str++; // remove leading spaces; verify there's data
    if (*str == '\0') { throw std::invalid_argument("str; no data"); } // nothing to convert

    // NOTE:  for some reason strtoull allows a negative sign, we don't; if
    //          converting to an unsigned then it must always be positive!
    if (!std::numeric_limits<T>::is_signed && *str == '-')
    { throw std::invalid_argument("str; negative"); }

    // reset errno and call fn (either strtoll or strtoull)
    errno = 0;
    char *ePtr;
    T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
                                              : strtoull(str, &ePtr, base);

    // check for any C errors -- note these are range errors on T, which may
    //   still be out of the range of the actual type we're using; the caller
    //   may need to perform additional range checks.
    if (errno != 0) 
    {
            if (errno == ERANGE) { throw std::range_error("str; out of range"); }
            else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
            else { throw std::invalid_argument("str; unknown errno"); }
    }

    // verify everything converted -- extraneous spaces are allowed
    if (ePtr != NULL)
    {
            while (isspace(*ePtr)) ePtr++;
            if (*ePtr != '\0') { throw std::invalid_argument("str; bad data"); }
    }

    return tmp;
}

template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
    static const long long max = std::numeric_limits<T>::max();
    static const long long min = std::numeric_limits<T>::min();

    long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp < min || tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit signed range (";
            if (sizeof(T) != 1) err << min << ".." << max;
            else err << (int) min << ".." << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
    static const unsigned long long max = std::numeric_limits<T>::max();

    unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type

    // final range check -- only needed if not long long type; a smart compiler
    //   should optimize this whole thing out
    if (sizeof(T) == sizeof(tmp)) { return tmp; }

    if (tmp > max)
    {
            std::ostringstream err;
            err << "str; value " << tmp << " out of " << sizeof(T) * 8
                << "-bit unsigned range (0..";
            if (sizeof(T) != 1) err << max;
            else err << (int) max;  // don't print garbage chars
            err << ")";
            throw std::range_error(err.str());
    }

    return tmp;
}

template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
    return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
                                             : StringToUnsigned<T>(str, base);
}

template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
    return out_convertedVal = StringToDecimal<T>(str, base);
}

/*============================== [ Test Strap ] ==============================*/ 

#include <inttypes.h>
#include <iostream>

static bool _g_anyFailed = false;

template<typename T>
void TestIt(const char *tName,
            const char *s, int base,
            bool successExpected = false, T expectedValue = 0)
{
    #define FAIL(s) { _g_anyFailed = true; std::cout << s; }

    T x;
    std::cout << "converting<" << tName << ">b:" << base << " [" << s << "]";
    try
    {
            StringToDecimal<T>(x, s, base);
            // get here on success only
            if (!successExpected)
            {
                    FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
            }
            else
            {
                    std::cout << " -> ";
                    if (sizeof(T) != 1) std::cout << x;
                    else std::cout << (int) x;  // don't print garbage chars
                    if (x != expectedValue)
                    {
                            FAIL("; FAILED (expected value:" << expectedValue << ")!");
                    }
                    std::cout << std::endl;
            }
    }
    catch (std::exception &e)
    {
            if (successExpected)
            {
                    FAIL(   " -- TEST FAILED; EXPECTED SUCCESS!"
                         << " (got:" << e.what() << ")" << std::endl);
            }
            else
            {
                    std::cout << "; expected exception encounterd: [" << e.what() << "]" << std::endl;
            }
    }
}

#define TEST(t, s, ...) \
    TestIt<t>(#t, s, __VA_ARGS__);

int main()
{
    std::cout << "============ variable base tests ============" << std::endl;
    TEST(int, "-0xF", 0, true, -0xF);
    TEST(int, "+0xF", 0, true, 0xF);
    TEST(int, "0xF", 0, true, 0xF);
    TEST(int, "-010", 0, true, -010);
    TEST(int, "+010", 0, true, 010);
    TEST(int, "010", 0, true, 010);
    TEST(int, "-10", 0, true, -10);
    TEST(int, "+10", 0, true, 10);
    TEST(int, "10", 0, true, 10);

    std::cout << "============ base-10 tests ============" << std::endl;
    TEST(int, "-010", 10, true, -10);
    TEST(int, "+010", 10, true, 10);
    TEST(int, "010", 10, true, 10);
    TEST(int, "-10", 10, true, -10);
    TEST(int, "+10", 10, true, 10);
    TEST(int, "10", 10, true, 10);
    TEST(int, "00010", 10, true, 10);

    std::cout << "============ base-8 tests ============" << std::endl;
    TEST(int, "777", 8, true, 0777);
    TEST(int, "-0111 ", 8, true, -0111);
    TEST(int, "+0010 ", 8, true, 010);

    std::cout << "============ base-16 tests ============" << std::endl;
    TEST(int, "DEAD", 16, true, 0xDEAD);
    TEST(int, "-BEEF", 16, true, -0xBEEF);
    TEST(int, "+C30", 16, true, 0xC30);

    std::cout << "============ base-2 tests ============" << std::endl;
    TEST(int, "-10011001", 2, true, -153);
    TEST(int, "10011001", 2, true, 153);

    std::cout << "============ irregular base tests ============" << std::endl;
    TEST(int, "Z", 36, true, 35);
    TEST(int, "ZZTOP", 36, true, 60457993);
    TEST(int, "G", 17, true, 16);
    TEST(int, "H", 17);

    std::cout << "============ space deliminated tests ============" << std::endl;
    TEST(int, "1337    ", 10, true, 1337);
    TEST(int, "   FEAD", 16, true, 0xFEAD);
    TEST(int, "   0711   ", 0, true, 0711);

    std::cout << "============ bad data tests ============" << std::endl;
    TEST(int, "FEAD", 10);
    TEST(int, "1234 asdfklj", 10);
    TEST(int, "-0xF", 10);
    TEST(int, "+0xF", 10);
    TEST(int, "0xF", 10);
    TEST(int, "-F", 10);
    TEST(int, "+F", 10);
    TEST(int, "12.4", 10);
    TEST(int, "ABG", 16);
    TEST(int, "10011002", 2);

    std::cout << "============ int8_t range tests ============" << std::endl;
    TEST(int8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(int8_t, "80", 16);
    TEST(int8_t, "-80", 16, true, std::numeric_limits<int8_t>::min());
    TEST(int8_t, "-81", 16);
    TEST(int8_t, "FF", 16);
    TEST(int8_t, "100", 16);

    std::cout << "============ uint8_t range tests ============" << std::endl;
    TEST(uint8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
    TEST(uint8_t, "80", 16, true, std::numeric_limits<int8_t>::max()+1);
    TEST(uint8_t, "-80", 16);
    TEST(uint8_t, "-81", 16);
    TEST(uint8_t, "FF", 16, true, std::numeric_limits<uint8_t>::max());
    TEST(uint8_t, "100", 16);

    std::cout << "============ int16_t range tests ============" << std::endl;
    TEST(int16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(int16_t, "8000", 16);
    TEST(int16_t, "-8000", 16, true, std::numeric_limits<int16_t>::min());
    TEST(int16_t, "-8001", 16);
    TEST(int16_t, "FFFF", 16);
    TEST(int16_t, "10000", 16);

    std::cout << "============ uint16_t range tests ============" << std::endl;
    TEST(uint16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
    TEST(uint16_t, "8000", 16, true, std::numeric_limits<int16_t>::max()+1);
    TEST(uint16_t, "-8000", 16);
    TEST(uint16_t, "-8001", 16);
    TEST(uint16_t, "FFFF", 16, true, std::numeric_limits<uint16_t>::max());
    TEST(uint16_t, "10000", 16);

    std::cout << "============ int32_t range tests ============" << std::endl;
    TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(int32_t, "80000000", 16);
    TEST(int32_t, "-80000000", 16, true, std::numeric_limits<int32_t>::min());
    TEST(int32_t, "-80000001", 16);
    TEST(int32_t, "FFFFFFFF", 16);
    TEST(int32_t, "100000000", 16);

    std::cout << "============ uint32_t range tests ============" << std::endl;
    TEST(uint32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
    TEST(uint32_t, "80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
    TEST(uint32_t, "-80000000", 16);
    TEST(uint32_t, "-80000001", 16);
    TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
    TEST(uint32_t, "100000000", 16);

    std::cout << "============ int64_t range tests ============" << std::endl;
    TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(int64_t, "8000000000000000", 16);
    TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
    TEST(int64_t, "-8000000000000001", 16);
    TEST(int64_t, "FFFFFFFFFFFFFFFF", 16);
    TEST(int64_t, "10000000000000000", 16);

    std::cout << "============ uint64_t range tests ============" << std::endl;
    TEST(uint64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
    TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
    TEST(uint64_t, "-8000000000000000", 16);
    TEST(uint64_t, "-8000000000000001", 16);
    TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
    TEST(uint64_t, "10000000000000000", 16);

    std::cout << std::endl << std::endl
              << (_g_anyFailed ? "!! SOME TESTS FAILED !!" : "ALL TESTS PASSED")
              << std::endl;

    return _g_anyFailed;
}

StringToDecimal사용자-랜드 방법입니다. 오버로드되어 다음과 같이 호출 될 수 있습니다.

int a; a = StringToDecimal<int>("100");

아니면 이거:

int a; StringToDecimal(a, "100");

나는 int 유형을 반복하는 것을 싫어하므로 후자를 선호합니다. 이렇게하면 'a'유형이 변경 되어도 결과가 나 빠지지 않습니다. 컴파일러가 다음과 같이 알아낼 수 있기를 바랍니다.

int a; a = StringToDecimal("100");

...하지만 C ++은 템플릿 반환 유형을 추론하지 않으므로 얻을 수있는 최선입니다.

구현은 매우 간단합니다.

CstrtoxllWrapper랩 모두 strtoullstrtoll 템플릿 유형의 로그인 네스 및 제공하는 몇 가지 추가 보장에 따라 필요한 중 전화 (서명되지 않은 경우 예를 들어, 음성 입력은 허용되지 그것은 전체 문자열 변환이었다 보장).

CstrtoxllWrapper하여 사용 StringToSigned하고 StringToUnsigned컴파일러 있음 (긴 길이 / 긴 길이 부호없는) 최대 유형; 이를 통해 최대 변환이 수행 될 수 있습니다. 그런 다음 필요한 경우 StringToSigned/ StringToUnsigned는 기본 유형에 대한 최종 범위 확인을 수행합니다. 마지막으로 엔드 포인트 방법StringToDecimal 는 기본 유형의 부호에 따라 호출 할 StringTo * 템플리트 메소드를 결정합니다.

대부분의 정크는 컴파일러에 의해 최적화 될 수 있다고 생각합니다. 거의 모든 것이 컴파일 타임 결정적이어야합니다. 이 측면에 대한 논평은 나에게 흥미로울 것입니다!


"가장 큰 유형 사용"-> long long대신에 왜 intmax_t?
chux-복원 Monica Monica

당신이 원하는 자신감 if (ePtr != str). 또한의 isspace((unsigned char) *ePtr)음수 값을 올바르게 처리 하는 데 사용하십시오 *ePtr.
chux-복원 Monica Monica

-3

C에서는 int atoi (const char * str),

내용을 정수로 해석하는 C- 문자열 str을 구문 분석하여 int 유형의 값으로 리턴합니다.


2
atoi질문에서 연결되면서 나는 그것을 알고 있습니다. 문제는 분명히 C에 관한 것이 아니라 C ++에 관한 것입니다. -1
유진 요코타
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.