Próbuję dowiedzieć się, czy istnieje alternatywny sposób konwersji ciągu na liczbę całkowitą w C.
Regularnie wzoruję następujące elementy w moim kodzie.
char s[] = "45";
int num = atoi(s);
Czy jest więc lepszy czy inny sposób?
Próbuję dowiedzieć się, czy istnieje alternatywny sposób konwersji ciągu na liczbę całkowitą w C.
Regularnie wzoruję następujące elementy w moim kodzie.
char s[] = "45";
int num = atoi(s);
Czy jest więc lepszy czy inny sposób?
Odpowiedzi:
Jest strtollepszy IMO. Również polubiłem strtonum, więc użyj go, jeśli go masz (ale pamiętaj, że nie jest przenośny):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
Możesz być także zainteresowany strtoumaxistrtoimax które są standardowymi funkcjami w C99. Na przykład możesz powiedzieć:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
W każdym razie trzymaj się z dala od atoi:
Call atoi (str) jest równoważny z:
(int) strtol(str, (char **)NULL, 10)z wyjątkiem tego, że obsługa błędów może się różnić. Jeśli wartości nie można przedstawić, zachowanie jest niezdefiniowane .
strtonum?
#<stdlib.h>. Możesz jednak użyć standardowej strtoumaxalternatywy.
Solidne strtolrozwiązanie oparte na C89
Z:
atoirodziną)strtol(np. brak wiodących białych znaków lub końcowych znaków śmieci)#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;
}
Na podstawie: https://stackoverflow.com/a/6154614/895245
str2int(). Pedantyczny: użycie isspace((unsigned char) s[0]).
(unsigned char)obsada może mieć znaczenie?
l > INT_MAXi l < INT_MINjest bezcelowym porównywaniem liczb całkowitych, ponieważ oba wyniki są zawsze fałszywe. Co się stanie, jeśli zmienię je l >= INT_MAXi l <= INT_MINskasuję ostrzeżenia? W ARM C long i int są 32-bitowymi podstawowymi typami danych w ARM C i C ++
l >= INT_MAXniepoprawną funkcjonalność: Przykład powrotu STR2INT_OVERFLOWz wejściem "32767"i 16-bit int. Użyj kompilacji warunkowej. Przykład .
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;byłoby lepiej, if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}aby umożliwić wywołanie kodu do używania errnona intout-of-zakresu. To samo dotyczy if (l < INT_MIN....
Nie używaj funkcji z ato...grupy. Są zepsute i praktycznie bezużyteczne. Użycie byłoby lepszym rozwiązaniem sscanf, choć nie jest ono również idealne.
Aby przekonwertować ciąg na liczbę całkowitą, strto...należy użyć funkcji z grupy. W twoim konkretnym przypadku będzie to strtolfunkcja.
sscanffaktycznie ma niezdefiniowane zachowanie, jeśli próbuje przekonwertować liczbę spoza zakresu swojego typu (na przykład sscanf("999999999999999999999", "%d", &n)).
atoinie zapewnia żadnych istotnych informacji zwrotnych na temat sukcesu / niepowodzenia i ma niezdefiniowane zachowanie w przypadku przepełnienia. sscanfzapewnia informacje zwrotne o sukcesach / niepowodzeniach (wartość zwracana, co czyni ją „umiarkowanie lepszą”), ale nadal ma niezdefiniowane zachowanie w przypadku przepełnienia. Tylko strtolrealne rozwiązanie.
sscanf. (Chociaż przyznaję, że czasami używam atoi, zwykle w przypadku programów, które nie spodziewają się przeżyć dłużej niż 10 minut przed usunięciem źródła).
Możesz dla zabawy napisać trochę 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);
}
Możesz także ustawić go jako rekurencyjny, który może być stary w 3 liniach =)
code((* str) - „0”)code
"----1" 2) Ma niezdefiniowane zachowanie z intprzepełnieniem, kiedy wynik powinien być INT_MIN. Zastanów sięmy_getnbr("-2147483648")
Chciałem tylko udostępnić rozwiązanie dla niepodpisanego na długo.
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 *.
48znaczy? Czy zakładasz, że jest to wartość miejsca, w '0'którym kod będzie działał? Proszę nie narzucać światu tak szerokich założeń!
'0'tak, jak powinieneś.
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;
}
Zawsze możesz rzucić własne!
#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;
}
To zrobi, co chcesz bez bałaganu.
strto...rodziny funkcji. Są przenośne i znacznie lepsze.
0x2d, 0x30zamiast '-', '0'. Nie pozwala na '+'znak. Po co (int)rzucać (int)strlen(snum)? UB, jeśli wejście jest "". UB, gdy wynik jest INT_MINspowodowany intprzepełnieniemaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Ta funkcja ci pomoże
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);
}
Patrz: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
atoii przyjaciół jest to, że jeśli występuje przepełnienie, jest to niezdefiniowane zachowanie. Twoja funkcja tego nie sprawdza. strtoli przyjaciele tak.
Ok, miałem ten sam problem. Wymyśliłem to rozwiązanie, działało dla mnie najlepiej, próbowałem atoi (), ale nie działało dobrze dla mnie, więc oto moje rozwiązanie:
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;
}
//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);vs. nInt = nInt*10 + snum[index] - '0'; if(!nInt)nie jest potrzebny.
W C ++ możesz użyć takiej funkcji:
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;
}
Może to pomóc w konwersji dowolnego łańcucha na dowolny typ, taki jak float, int, double ...
Tak, możesz zapisać liczbę całkowitą bezpośrednio:
int num = 45;
Jeśli musisz przeanalizować ciąg znaków atoilub strolwygrywa konkurs „Najkrótsza ilość kodu”.
strtol()faktycznie wymaga sporej ilości kodu. Może on powrócić LONG_MINalbo LONG_MAX też , jeśli to wartość rzeczywista przerobiony lub nie występuje niedomiar lub nadmiar, i może zwrócić 0 albo jeśli jest to wartość rzeczywistą lub jeśli nie ma numer konwersji. Musisz ustawić errno = 0przed połączeniem i sprawdzić endptr.