Vos balises et votre titre indiquent que vous voulez une solution en C, mais votre question dit C ou C ++. Lequel veut-tu?
In silico
1
@Yann, Désolé pour cette confusion. Je préférerai C.
user618677
1
Cela fonctionne, mais ce n'est pas la méthode recommandée, car il n'y a aucun moyen de gérer les erreurs. N'utilisez jamais cela dans le code de production, sauf si vous pouvez faire confiance à 100% à l'entrée.
Uwe Geuder
1
Définissez «mieux» et expliquez clairement pourquoi vous avez besoin d'une autre méthode.
Marquis de Lorne
3
@EJP Juste pour m'améliorer.
user618677
Réponses:
185
Il y a strtolquel 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):
que dois-je inclure strtonum? Je reçois toujours un avertissement de déclaration implicite
jsj
@ trideceth12 Sur les systèmes où il est disponible, il doit être déclaré dans #<stdlib.h>. Cependant, vous pouvez utiliser l' strtoumaxalternative standard .
cnicutar
4
Cette réponse ne semble pas plus courte que le premier code de l'interrogateur.
Azurespot
11
@NoniA. La concision est toujours bonne, mais pas au détriment de l'exactitude.
cnicutar
6
Pas tant de mal que de danger. atoi () fonctionne si l'entrée est valide. Mais que faire si vous faites de l'atoi ("chat")? strtol () a un comportement défini si la valeur ne peut pas être représentée comme un long, atoi () ne le fait pas.
Daniel B.
27
strtolSolution robuste basée sur C89
Avec:
pas de comportement indéfini (comme cela pourrait être le cas avec la atoifamille)
une définition plus stricte de l'entier que strtol(par exemple, aucun espace de tête ni caractère de fin de corbeille)
classification du cas d'erreur (par exemple pour donner des messages d'erreur utiles aux utilisateurs)
une "suite de tests"
#include<assert.h>#include<ctype.h>#include<errno.h>#include<limits.h>#include<stdio.h>#include<stdlib.h>typedefenum{
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",(longint)INT_MAX +1L);
assert(str2int(&i, s,10)== STR2INT_OVERFLOW);}/* int underflow */if(LONG_MIN < INT_MIN){
sprintf(s,"%ld",(longint)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;}
Belle robustesse str2int(). Pedantic: utilisation isspace((unsigned char) s[0]).
chux
@chux merci! Pouvez-vous expliquer un peu plus pourquoi le (unsigned char)casting peut faire la différence?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Le compilateur IAR C l'avertit l > INT_MAXet l < INT_MINconstitue 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_MAXet l <= INT_MINefface les avertissements? Sur ARM C, long et int sont des types de données de base
ecle
@ecle change le code pour l >= INT_MAXentraîner une fonctionnalité incorrecte: Exemple de retour STR2INT_OVERFLOWavec entrée "32767"et 16 bits int. Utilisez une compilation conditionnelle. Exemple .
chux
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 errnosur inthors de portée. Pareil pour if (l < INT_MIN....
chux
24
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 strtolfonction.
sscanfa 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)).
Keith Thompson
1
@Keith Thompson: C'est exactement ce que je veux dire. atoine fournit aucun retour significatif de réussite / échec et a un comportement indéfini en cas de débordement. sscanffournit 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 strtolest une solution viable.
AnT
1
D'accord; Je voulais juste souligner le problème potentiellement fatal avec 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.)
Keith Thompson
5
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 =)
Merci beaucoup .. Mais pourriez-vous me dire comment fonctionne le code ci-dessous? code((* str) - '0')code
user618677
un personnage a une valeur ascii. Si vous n'êtes pas de type linux: man ascii dans le shell ou sinon allez sur: table-ascii.com . Vous verrez que le caractère '0' = 68 (je pense) pour un int. Donc, pour obtenir le nombre de «9» (c'est «0» + 9), vous obtenez 9 = «9» - «0». Vous comprenez?
jDourlens
1
1) Le code autorise "----1" 2) A un comportement indéfini avec intdébordement lorsque le résultat devrait être INT_MIN. Considérezmy_getnbr("-2147483648")
chux - Rétablissez Monica
Merci pour la précision, c'était juste pour montrer un petit exemple. Comme il est dit pour le plaisir et l'apprentissage. Vous devriez certainement utiliser la bibliothèque standard pour ce type de tâches. Plus rapide et plus sûr!
jDourlens
2
Je voulais juste partager une solution pour longtemps non signé.
unsignedlongToUInt(char* str){unsignedlong mult =1;unsignedlong 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;}
Ne gère pas le débordement. En outre, le paramètre devrait être const char *.
Roland Illig
2
De plus, qu'est-ce que cela 48signifie? 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!
Toby Speight
@TobySpeight Oui, je suppose que 48 représentent «0» dans le tableau ascii.
Jacob
3
Tout le monde n'est pas ASCII - utilisez-le '0'comme vous le devriez.
Toby Speight
il est recommandé d'utiliser à la place la fonction strtoul .
rapidclock
1
int atoi(constchar* 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(constchar* snum){int idx, strIdx =0, accum =0, numIsNeg =0;constunsignedint 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;elsereturn accum *-1;}int main(){/* Tests... */
printf("Returned number is: %d\n", my_atoi("34574"));
printf("Returned number is: %d\n", my_atoi("-23"));return0;}
Mais pourquoi? Cela ne vérifie pas le débordement et ignore simplement les valeurs de déchets. Il n'y a aucune raison de ne pas utiliser la strto...famille de fonctions. Ils sont portables et nettement meilleurs.
tchad
1
Étrange à utiliser 0x2d, 0x30au 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_MINdû à un intdébordement avecaccum += (snum[strIdx] - 0x30) * pow(10, idx);
chux - Rétablir Monica
@chux - Ce code est un code de démonstration. Il existe des solutions faciles à ce que vous avez décrit comme des problèmes potentiels.
ButchDean
2
@ButchDean Ce que vous décrivez comme "code de démonstration" sera utilisé par d'autres qui n'ont aucune idée de tous les détails. Seuls le score négatif et les commentaires sur cette réponse les protègent maintenant. À mon avis, le "code de démonstration" doit avoir une qualité beaucoup plus élevée.
Roland Illig
@RolandIllig Plutôt que d'être critique, ne serait-il pas plus utile pour les autres de mettre en place votre propre solution?
ButchDean
-1
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;elsereturn-1;break;default:if(c >='0'&& c <='9') ret +=(c -'0')* place;elsereturn-1;}}return sign * ret;}int strtoint(char* str){char* temp = str;int n =0;while(*temp !='\0'){
n++;
temp++;}return strtoint_n(str, n);}
Mais pourquoi faire ça? L'un des plus gros problèmes avec atoiet amis est que s'il y a débordement, c'est un comportement indéfini. Votre fonction ne vérifie pas cela. strtolet les amis le font.
tchad
1
Ouaip. Puisque C n'est pas Python, j'espère que les gens qui utilisent le langage C sont conscients de ce genre d'erreurs de débordement. Tout a ses propres limites.
Amith Chinthaka
-1
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(constchar* 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"));return0;}
Si vous voulez le faire en toute sécurité, strtol()nécessite en fait une bonne quantité de code. Il peut revenir LONG_MINou LONG_MAXnon 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 = 0avant l'appel et vérifier endptr.
Keith Thompson
Les solutions données pour analyser, ne sont pas des solutions viables.
Réponses:
Il y a
strtol
quel est le meilleur IMO. J'ai également pris goûtstrtonum
, alors utilisez-le si vous l'avez (mais rappelez-vous qu'il n'est pas portable):ÉDITER
Vous pourriez également être intéressé par
strtoumax
etstrtoimax
qui sont des fonctions standard dans C99. Par exemple, vous pourriez dire:Quoi qu'il en soit, restez à l'écart de
atoi
:la source
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 C89Avec:
atoi
famille)strtol
(par exemple, aucun espace de tête ni caractère de fin de corbeille)GitHub en amont .
Basé sur: https://stackoverflow.com/a/6154614/895245
la source
str2int()
. Pedantic: utilisationisspace((unsigned char) s[0])
.(unsigned char)
casting peut faire la différence?l > INT_MAX
etl < 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 modifiel >= INT_MAX
etl <= INT_MIN
efface les avertissements? Sur ARM C, long et int sont des types de données de basel >= INT_MAX
entraîner une fonctionnalité incorrecte: Exemple de retourSTR2INT_OVERFLOW
avec entrée"32767"
et 16 bitsint
. Utilisez une compilation conditionnelle. Exemple .if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
serait mieux queif (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
de permettre à un code appelant à utilisererrno
surint
hors de portée. Pareil pourif (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'utilisersscanf
, 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 lastrtol
fonction.la source
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. Seulestrtol
est une solution viable.sscanf
. (Bien que j'avoue que j'utilise parfoisatoi
, 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:
Vous pouvez également le rendre récursif qui peut vieillir en 3 lignes =)
la source
code
((* str) - '0')code
"----1"
2) A un comportement indéfini avecint
débordement lorsque le résultat devrait êtreINT_MIN
. Considérezmy_getnbr("-2147483648")
Je voulais juste partager une solution pour longtemps non signé.
la source
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.la source
Vous pouvez toujours rouler le vôtre!
Cela fera ce que vous voulez sans encombrement.
la source
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 estINT_MIN
dû à unint
débordement avecaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Cette fonction vous aidera
Réf: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
la source
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:
la source
la source
nInt = (nInt *= 10) + ((int) snum[index] - 48);
vsnInt = nInt*10 + snum[index] - '0';
if(!nInt)
non nécessaire.En C ++, vous pouvez utiliser une telle fonction:
Cela peut vous aider à convertir n'importe quelle chaîne en n'importe quel type tel que float, int, double ...
la source
Oui, vous pouvez stocker directement l'entier:
Si vous devez analyser une chaîne,
atoi
ou si vousstrol
allez gagner le concours "la plus petite quantité de code".la source
strtol()
nécessite en fait une bonne quantité de code. Il peut revenirLONG_MIN
ouLONG_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églererrno = 0
avant l'appel et vérifierendptr
.