J'essaie de savoir s'il existe un autre moyen de convertir une chaîne en entier en C.
Je modèle régulièrement les éléments suivants dans mon code.
char s[] = "45";
int num = atoi(s);
Alors, y a-t-il une meilleure façon ou une autre façon?
J'essaie de savoir s'il existe un autre moyen de convertir une chaîne en entier en C.
Je modèle régulièrement les éléments suivants dans mon code.
char s[] = "45";
int num = atoi(s);
Alors, y a-t-il une meilleure façon ou une autre façon?
Réponses:
Il y a strtol
quel est le meilleur IMO. J'ai également pris goût strtonum
, alors utilisez-le si vous l'avez (mais rappelez-vous qu'il n'est pas portable):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
Vous pourriez également être intéressé par strtoumax
etstrtoimax
qui sont des fonctions standard dans C99. Par exemple, vous pourriez dire:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
Quoi qu'il en soit, restez à l'écart de atoi
:
L'appel atoi (str) doit être équivalent à:
(int) strtol(str, (char **)NULL, 10)
sauf que la gestion des erreurs peut différer. Si la valeur ne peut pas être représentée, le comportement n'est pas défini .
strtonum
? Je reçois toujours un avertissement de déclaration implicite
#<stdlib.h>
. Cependant, vous pouvez utiliser l' strtoumax
alternative standard .
strtol
Solution robuste basée sur C89
Avec:
atoi
famille)strtol
(par exemple, aucun espace de tête ni caractère de fin de corbeille)#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: utilisation isspace((unsigned char) s[0])
.
(unsigned char)
casting peut faire la différence?
l > INT_MAX
et l < INT_MIN
constitue une comparaison d'entier inutile car l'un ou l'autre résultat est toujours faux. Que se passe-t-il si je les modifie l >= INT_MAX
et l <= INT_MIN
efface les avertissements? Sur ARM C, long et int sont des types de données de base
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
serait mieux que if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
de permettre à un code appelant à utiliser errno
sur int
hors de portée. Pareil pour if (l < INT_MIN...
.
N'utilisez pas les fonctions du ato...
groupe. Ceux-ci sont cassés et pratiquement inutiles. Une solution modérément meilleure serait d'utiliser sscanf
, bien qu'elle ne soit pas parfaite non plus.
Pour convertir une chaîne en entier, les fonctions du strto...
groupe doivent être utilisées. Dans votre cas spécifique, ce serait la strtol
fonction.
sscanf
a en fait un comportement indéfini s'il essaie de convertir un nombre en dehors de la plage de son type (par exemple, sscanf("999999999999999999999", "%d", &n)
).
atoi
ne fournit aucun retour significatif de réussite / échec et a un comportement indéfini en cas de débordement. sscanf
fournit une sorte de rétroaction de réussite / échec (la valeur de retour, ce qui la rend "modérément meilleure"), mais a toujours un comportement indéfini en cas de débordement. Seule strtol
est une solution viable.
sscanf
. (Bien que j'avoue que j'utilise parfois atoi
, généralement pour des programmes auxquels je ne m'attends pas à survivre plus de 10 minutes avant de supprimer la source.)
Vous pouvez coder un peu atoi () pour le plaisir:
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);
}
Vous pouvez également le rendre récursif qui peut vieillir en 3 lignes =)
code
((* str) - '0')code
"----1"
2) A un comportement indéfini avec int
débordement lorsque le résultat devrait être INT_MIN
. Considérezmy_getnbr("-2147483648")
Je voulais juste partager une solution pour longtemps non signé.
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
signifie? Supposez-vous que c'est la valeur de l' '0'
emplacement d'exécution du code? Veuillez ne pas infliger des hypothèses aussi larges au monde!
'0'
comme vous le devriez.
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;
}
Vous pouvez toujours rouler le vôtre!
#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;
}
Cela fera ce que vous voulez sans encombrement.
strto...
famille de fonctions. Ils sont portables et nettement meilleurs.
0x2d, 0x30
au lieu de '-', '0'
. Ne permet pas de '+'
signe. Pourquoi (int)
participer (int)strlen(snum)
? UB si l'entrée est ""
. UB lorsque le résultat est INT_MIN
dû à un int
débordement avecaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Cette fonction vous aidera
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);
}
Réf: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
atoi
et amis est que s'il y a débordement, c'est un comportement indéfini. Votre fonction ne vérifie pas cela. strtol
et les amis le font.
Ok, j'ai eu le même problème.J'ai trouvé cette solution.Elle a fonctionné le mieux pour moi.J'ai essayé atoi () mais n'a pas bien fonctionné pour moi.Voici donc ma solution:
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 nécessaire.
En C ++, vous pouvez utiliser une telle fonction:
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;
}
Cela peut vous aider à convertir n'importe quelle chaîne en n'importe quel type tel que float, int, double ...
Oui, vous pouvez stocker directement l'entier:
int num = 45;
Si vous devez analyser une chaîne, atoi
ou si vous strol
allez gagner le concours "la plus petite quantité de code".
strtol()
nécessite en fait une bonne quantité de code. Il peut revenir LONG_MIN
ou LONG_MAX
non si c'est la valeur convertie réelle ou s'il y a un trop - plein ou soupassement, et il peut retourner 0 soit si c'est la valeur réelle ou s'il n'y avait pas de numéro à convertir. Vous devez régler errno = 0
avant l'appel et vérifier endptr
.