Sto cercando di scoprire se esiste un modo alternativo di convertire la stringa in intero in C.
Nel mio codice modifico regolarmente quanto segue.
char s[] = "45";
int num = atoi(s);
Quindi, c'è un modo migliore o un altro?
Sto cercando di scoprire se esiste un modo alternativo di convertire la stringa in intero in C.
Nel mio codice modifico regolarmente quanto segue.
char s[] = "45";
int num = atoi(s);
Quindi, c'è un modo migliore o un altro?
Risposte:
C'è strtol
qual è l'IMO migliore. Inoltre mi è piaciuto strtonum
, quindi usalo se ce l'hai (ma ricorda che non è portatile):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
Potresti anche essere interessato a strtoumax
estrtoimax
quali sono le funzioni standard in C99. Ad esempio potresti dire:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
Ad ogni modo, stai lontano da atoi
:
Il call atoi (str) deve essere equivalente a:
(int) strtol(str, (char **)NULL, 10)
salvo che la gestione degli errori può differire. Se il valore non può essere rappresentato, il comportamento non è definito .
strtonum
? Continuo a ricevere un avviso di dichiarazione implicita
#<stdlib.h>
. Tuttavia, è possibile utilizzare l' strtoumax
alternativa standard .
Robusta strtol
soluzione basata su C89
Con:
atoi
famiglia)strtol
(ad es. nessuno spazio bianco iniziale o caratteri di cestino finali)#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;
}
Basato su: https://stackoverflow.com/a/6154614/895245
str2int()
. Pedante: usare isspace((unsigned char) s[0])
.
(unsigned char)
cast potrebbe fare la differenza?
l > INT_MAX
e l < INT_MIN
sono il confronto intero inutile dal momento che sia risultato è sempre false. Cosa succede se li cambio in l >= INT_MAX
e l <= INT_MIN
per cancellare gli avvisi? Su ARM C, long e int sono tipi di dati Basic con
l >= INT_MAX
comporta una funzionalità errata: esempio di ritorno STR2INT_OVERFLOW
con input "32767"
e 16 bit int
. Utilizzare una compilazione condizionale. Esempio .
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
sarebbe meglio if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
consentire il codice chiamante da utilizzare errno
al di int
fuori dell'intervallo. Lo stesso per if (l < INT_MIN...
.
Non utilizzare le funzioni dal ato...
gruppo. Questi sono rotti e praticamente inutili. Una soluzione moderatamente migliore sarebbe usare sscanf
, anche se non è perfetta neanche.
Per convertire la stringa in numero intero, è strto...
necessario utilizzare le funzioni del gruppo. Nel tuo caso specifico sarebbe strtol
funzionale.
sscanf
ha effettivamente un comportamento indefinito se tenta di convertire un numero al di fuori dell'intervallo del suo tipo (ad esempio sscanf("999999999999999999999", "%d", &n)
).
atoi
non fornisce alcun feedback di successo / fallimento significativo e ha un comportamento indefinito in caso di overflow. sscanf
fornisce una sorta di feedback di successo / fallimento (il valore di ritorno, che è ciò che lo rende "moderatamente migliore"), ma ha ancora un comportamento indefinito in caso di overflow. Solo strtol
è una soluzione praticabile.
sscanf
. (Anche se confesso che a volte uso atoi
, di solito per programmi che non mi aspetto di sopravvivere più di 10 minuti prima di eliminare la fonte.)
Puoi scrivere un po 'di atoi () per divertimento:
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);
}
Puoi anche renderlo ricorsivo che può essere vecchio in 3 righe =)
code
((* str) - '0')code
"----1"
2) Ha un comportamento indefinito con int
overflow quando il risultato dovrebbe essere INT_MIN
. Consideramy_getnbr("-2147483648")
Volevo solo condividere una soluzione per lungo tempo senza firma.
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
significa? Stai assumendo che sia il valore di '0'
dove verrà eseguito il codice? Per favore, non infliggere ipotesi così vaste al mondo!
'0'
come dovresti.
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;
}
Puoi sempre creare il tuo!
#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;
}
Questo farà ciò che vuoi senza ingombro.
strto...
famiglia di funzioni. Sono portatili e significativamente migliori.
0x2d, 0x30
invece di '-', '0'
. Non consente il '+'
segno. Perché (int)
partecipare (int)strlen(snum)
? UB se l'ingresso è ""
. UB quando il risultato è INT_MIN
dovuto allo int
straripamento diaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Questa funzione ti aiuterà
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);
}
Rif: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
atoi
e gli amici è che se c'è troppo pieno, è un comportamento indefinito. La tua funzione non controlla questo. strtol
e gli amici lo fanno.
Ok, ho avuto lo stesso problema, ho trovato questa soluzione, ha funzionato per me al meglio, ho provato atoi () ma non ha funzionato bene per me, quindi ecco la mia soluzione:
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)
non necessario.
In C ++, è possibile utilizzare una tale funzione:
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;
}
Questo può aiutarti a convertire qualsiasi stringa in qualsiasi tipo come float, int, double ...
Sì, è possibile memorizzare direttamente l'intero:
int num = 45;
Se devi analizzare una stringa atoi
o strol
stai per vincere il concorso "quantità più breve di codice".
strtol()
realtà richiede una discreta quantità di codice. Si può tornare LONG_MIN
o LONG_MAX
sia se questo è il valore reale convertito o se c'è un underflow o troppo pieno, e può restituire 0 o se questo è il valore reale o se non ci fosse il numero da convertire. È necessario impostare errno = 0
prima della chiamata e controllare il endptr
.