IT TIP

std :: stoi는 실제로 사용하기에 안전합니까?

itqueen 2020. 12. 12. 12:52
반응형

std :: stoi는 실제로 사용하기에 안전합니까?


의 몰락에 대해 누군가와 멋진 대화를 나눴습니다 std::stoi. 솔직히 말하면 std::strtol내부적으로 사용 하고 오류가보고되면 발생합니다. 그들에 따르면, 그러나, std::strtol의 입력 오류를보고하지 말아야 "abcxyz"원인 stoi던져 없습니다 std::invalid_argument.

우선, 다음은 이러한 경우의 동작에 대해 GCC에서 테스트 한 두 가지 프로그램입니다.
strtol
stoi

둘 다에서 성공 "123"과 실패를 보여줍니다 "abc".


더 많은 정보를 가져 오기 위해 표준을 살펴 보았습니다.

§ 21.5

Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that  
no conversion could be performed. Throws out_of_range if the converted value is  
outside the range of representable values for the return type.

이는에 의존하는 동작을 요약합니다 strtol. 이제 어떻 strtol습니까? C11 초안에서 이것을 찾았습니다.

§7.22.1.4

If the subject sequence is empty or does not have the expected form, no  
conversion is performed; the value of nptr is stored in the object  
pointed to by endptr, provided that endptr is not a null pointer.

를 전달하는 상황을 감안할 때 "abc"C 표준 nptr은 문자열의 시작을 endptr가리키는, 포인터가 전달 된에 저장되도록 지정 합니다. 이것은 테스트와 일치하는 것 같습니다. 또한 다음과 같이 0이 반환되어야합니다.

§7.22.1.4

If no conversion could be performed, zero is returned.

그것은 이러한 조건은 현재 C ++ 11 표준을 준수 0 반환해야하므로 이전의 참조는, 어떤 변환이 수행 될 것이라고 말했다 stoi던지고 std::invalid_argument.


이 결과는 stoi다른 문자열에서 int 로의 변환에 대한 더 나은 대안 으로 추천 하거나 예상 한 방식대로 작동하는 것처럼 직접 사용하고 싶지 않기 때문에 중요합니다. 잘못된 변환으로 텍스트를 포착합니다.

그래서이 모든 후에 내가 어딘가에서 잘못 되었습니까? 이 예외가 던져 졌다는 좋은 증거가있는 것 같습니다. 내 증명이 유효합니까, 아니면 std::stoi예외가 주어질 때 그 예외를 던질 것이라는 보장이 "abc"없습니까?


std::stoi입력에 오류가 발생 합니까 "abcxyz"?

예.

오버플로를 제외하고 는 오류를strtol 보고하지 않는다는 사실 때문에 혼란 스러울 수 있습니다 . 변환이 수행되지 않았 음을보고 할 수 있지만 이는 C 표준에서 오류 조건으로 언급되지 않습니다.

strtol세 가지 C 표준 모두 유사하게 정의되며 지루한 세부 사항은 아끼지 않지만 기본적으로 실제 숫자에 해당하는 입력 문자열의 하위 문자열 인 "주제 시퀀스"를 정의합니다. 다음 네 가지 조건은 동일합니다.

  • 제목 순서가 예상되는 형식 (일반 영어 : 숫자 임)
  • 주제 시퀀스가 ​​비어 있지 않습니다.
  • 전환이 발생했습니다
  • *endptr != nptr(이것은 endptrnull이 아닌 경우에만 의미 가 있습니다)

오버플로가 발생하면 여전히 변환이 발생했다고합니다.

이제 "abcxyz"숫자가 포함되어 있지 않기 때문에 문자열의 제목 시퀀스 "abcxyz"가 비어 있어야 변환이 수행되지 않습니다. 다음 C90 / C99 / C11 프로그램은이를 실험적으로 확인합니다.

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *nptr = "abcxyz", *endptr[1];
    strtol(nptr, endptr, 0);
    if (*endptr == nptr)
        printf("No conversion could be performed.\n");
    return 0;
}

이는 선택적 기본 인수없이 입력이 제공 될 때 모든 준수 구현이 throw std::stoi 되어야 함을 의미합니다 .invalid_argument"abcxyz"


이것은 std::stoi만족스러운 오류 검사를 의미합니까 ?

No. The person you were talking to is correct when she says that std::stoi is more lenient than performing the full check errno == 0 && end != start && *end=='\0' after std::strtol, because std::stoi silently strips away all characters starting from the first non-numeric character in the string.

In fact off the top of my head the only language whose native conversion behaves somewhat like std::stoi is Javascript, and even then you have to force base 10 with parseInt(n, 10) to avoid the special case of hexadecimal numbers:

input      |  std::atoi       std::stoi      Javascript      full check 
===========+=============================================================
hello      |  0               error          error(NaN)      error      
0xygen     |  0               0              error(NaN)      error      
0x42       |  0               0              66              error      
42x0       |  42              42             42              error      
42         |  42              42             42              42         
-----------+-------------------------------------------------------------
languages  |  Perl, Ruby,     Javascript     Javascript      C#, Java,  
           |  PHP, C...       (base 10)                      Python...  

Note: there are also differences among languages in the handling of whitespace and redundant + signs.


Ok, so I want full error checking, what should I use?

I'm not aware of any built-in function that does this, but boost::lexical_cast<int> will do what you want. It is particularly strict since it even rejects surrounding whitespace, unlike Python's int() function. Note that invalid characters and overflows result in the same exception, boost::bad_lexical_cast.

#include <boost/lexical_cast.hpp>

int main() {
    std::string s = "42";
    try {
        int n = boost::lexical_cast<int>(s);
        std::cout << "n = " << n << std::endl;
    } catch (boost::bad_lexical_cast) {
        std::cout << "conversion failed" << std::endl;
    }
}

참고URL : https://stackoverflow.com/questions/11598990/is-stdstoi-actually-safe-to-use

반응형