C에서 문자열을 정수로 변환하는 다른 방법이 있는지 확인하려고합니다.
나는 정기적으로 내 코드에서 다음을 패턴 화합니다.
char s[] = "45";
int num = atoi(s);
더 나은 방법이 있습니까? 아니면 다른 방법이 있습니까?
C에서 문자열을 정수로 변환하는 다른 방법이 있는지 확인하려고합니다.
나는 정기적으로 내 코드에서 다음을 패턴 화합니다.
char s[] = "45";
int num = atoi(s);
더 나은 방법이 있습니까? 아니면 다른 방법이 있습니까?
답변:
이 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
합니까? 암시 적 선언 경고가 계속 표시됩니다.
#<stdlib.h>
. 그러나 표준 strtoumax
대안을 사용할 수 있습니다 .
강력한 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;
}
str2int()
합니다. Pedantic : 사용하십시오 isspace((unsigned char) s[0])
.
(unsigned char)
캐스트가 차이를 만들 수있는 이유를 좀 더 설명해 주 시겠습니까?
l > INT_MAX
하고 l < INT_MIN
하나 결과는 항상 false이기 때문에 의미가 정수 비교입니다. 내가으로 변경하면 어떻게됩니까 l >= INT_MAX
과 l <= INT_MIN
경고를 취소? ARM C에서 long 및 int 는 ARM C 및 C ++의
l >= INT_MAX
잘못된 기능 이 발생합니다 (예 : STR2INT_OVERFLOW
input "32767"
및 16-bit 반환) int
. 조건부 컴파일을 사용하십시오. 예 .
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...
.
ato...
그룹의 기능을 사용하지 마십시오 . 이것들은 깨졌으며 사실상 쓸모가 없습니다. sscanf
완벽하지는 않지만 적당히 더 나은 솔루션을 사용하는 것이 좋습니다 .
문자열을 정수로 변환하려면 strto...
그룹의 함수를 사용해야합니다. 특정 경우에는 strtol
기능입니다.
sscanf
실제로 유형 범위 밖의 숫자를 변환하려고하면 실제로 정의되지 않은 동작이 있습니다 (예 sscanf("999999999999999999999", "%d", &n)
:).
atoi
의미있는 성공 / 실패 피드백을 제공하지 않으며 오버플로시 정의되지 않은 동작을 갖습니다.sscanf
정렬의 성공 / 실패 피드백 (반환 값, "보통 값"이 좋음)을 제공하지만 오버플로시 여전히 정의되지 않은 동작이 있습니다. strtol
실행 가능한 솔루션 만 있습니다.
sscanf
. (저는 때때로 atoi
소스를 삭제하기 전에 10 분 이상 생존 할 것으로 예상되지 않는 프로그램에 대해 종종 고백 합니다)
재미를 위해 작은 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
"----1"
2) int
결과가이어야 할 때 오버플로로 정의되지 않은 동작 이 있습니다 INT_MIN
. 고려my_getnbr("-2147483648")
서명되지 않은 오랫동안 솔루션을 공유하고 싶었습니다.
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;
}
const char *
.
48
뜻입니까? 그것이 '0'
코드가 실행될 곳의 가치라고 가정 합니까? 세상에 그런 광범위한 가정을 가하지 마십시오!
'0'
. 원하는대로 사용하십시오 .
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;
}
당신은 항상 자신의 롤을 할 수 있습니다!
#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;
}
이것은 혼란없이 원하는 것을 할 것입니다.
strto...
기능 군 을 사용하지 않는 이유는 없습니다 . 휴대 성이 뛰어나고 훨씬 뛰어납니다.
0x2d, 0x30
대신에 사용하는 것이 이상합니다 '-', '0'
. '+'
부호를 허용하지 않습니다 . 왜 (int)
캐스트 (int)strlen(snum)
? UB 입력되면 ""
. 결과가 오버플로 INT_MIN
로 인한 경우 UBint
accum += (snum[strIdx] - 0x30) * pow(10, idx);
이 기능은 당신을 도울 것입니다
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
atoi
친구 와의 가장 큰 문제 중 하나는 오버플로가 발생하면 정의되지 않은 동작이라는 것입니다. 함수가이를 확인하지 않습니다. strtol
친구들은
//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;
}
nInt = (nInt *= 10) + ((int) snum[index] - 48);
대이 nInt = nInt*10 + snum[index] - '0';
if(!nInt)
필요하지 않습니다.
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과 같은 유형으로 변환하는 데 도움이 될 수 있습니다 ...
예, 정수를 직접 저장할 수 있습니다.
int num = 45;
문자열을 구문 분석해야 atoi
하거나 strol
"최단 코드 양"콘테스트에서 우승하려는 경우.
strtol()
하려면 실제로 상당한 양의 코드가 필요합니다. 그것은 반환 할 수 있습니다 LONG_MIN
또는 LONG_MAX
하나 그 실제 변환 된 값의 경우, 또는이 언더 플로우 또는 오버 플로우, 그리고 그 실제 값이 있다면이 중 하나를 0을 반환 할 수 있습니다 또는 변환에는 수 없었다 경우합니다. errno = 0
통화하기 전에 설정 하고을 확인해야합니다 endptr
.