Quelle est la façon C ++ d'analyser une chaîne (donnée comme char *) dans un int? Une gestion des erreurs robuste et claire est un plus (au lieu de retourner zéro ).
261
Quelle est la façon C ++ d'analyser une chaîne (donnée comme char *) dans un int? Une gestion des erreurs robuste et claire est un plus (au lieu de retourner zéro ).
Réponses:
Dans le nouveau C ++ 11, il existe des fonctions pour cela: stoi, stol, stoll, stoul et ainsi de suite.
Il lèvera une exception en cas d'erreur de conversion.
Même ces nouvelles fonctions ont toujours le même problème que noté par Dan: elles convertiront avec plaisir la chaîne "11x" en entier "11".
Voir plus: http://en.cppreference.com/w/cpp/string/basic_string/stol
la source
size_t
n'est pas égal à la longueur de la chaîne, il s'est arrêté tôt. Il retournera toujours 11 dans ce cas, maispos
sera 2 au lieu de la longueur de chaîne 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29Ce qu'il ne faut pas faire
Voici mon premier conseil: n'utilisez pas stringstream pour cela . Bien qu'au début il puisse sembler simple à utiliser, vous constaterez que vous devez faire beaucoup de travail supplémentaire si vous voulez de la robustesse et une bonne gestion des erreurs.
Voici une approche qui semble intuitivement fonctionner:
Cela a un problème majeur:
str2int(i, "1337h4x0r")
reviendra avec plaisirtrue
eti
obtiendra la valeur1337
. Nous pouvons contourner ce problème en nous assurant qu'il n'y a plus de caractèresstringstream
après la conversion:Nous avons résolu un problème, mais il y a encore quelques autres problèmes.
Que faire si le nombre dans la chaîne n'est pas en base 10? Nous pouvons essayer d'accueillir d'autres bases en réglant le flux sur le mode correct (par exemple
ss << std::hex
) avant d'essayer la conversion. Mais cela signifie que l'appelant doit savoir a priori quelle est la base du numéro - et comment l'appelant peut-il savoir cela? L'appelant ne sait pas encore quel est le numéro. Ils ne savent même pas que c'estun numéro! Comment peut-on s'attendre à savoir de quelle base il s'agit? Nous pourrions simplement exiger que tous les nombres entrés dans nos programmes soient en base 10 et rejeter les entrées hexadécimales ou octales comme invalides. Mais ce n'est pas très flexible ou robuste. Il n'y a pas de solution simple à ce problème. Vous ne pouvez pas simplement essayer la conversion une fois pour chaque base, car la conversion décimale réussira toujours pour les nombres octaux (avec un zéro non significatif) et la conversion octale peut réussir pour certains nombres décimaux. Alors maintenant, vous devez vérifier pour un zéro de tête. Mais attendez! Les nombres hexadécimaux peuvent également commencer par un zéro de tête (0x ...). Soupir.Même si vous parvenez à résoudre les problèmes ci-dessus, il y a encore un autre problème plus important: que se passe-t-il si l'appelant doit faire la distinction entre une mauvaise entrée (par exemple "123foo") et un numéro qui est hors de la plage de
int
(par exemple "4000000000" pour 32 bitsint
)? Avecstringstream
, il n'y a aucun moyen de faire cette distinction. Nous savons seulement si la conversion a réussi ou échoué. S'il échoue, nous n'avons aucun moyen de savoir pourquoi il a échoué. Comme vous pouvez le voir,stringstream
laisse beaucoup à désirer si vous voulez une robustesse et une gestion claire des erreurs.Cela m'amène à mon deuxième conseil: n'utilisez pas Boost
lexical_cast
pour cela . Considérez ce que lalexical_cast
documentation a à dire:Quoi?? Nous avons déjà vu que le
stringstream
niveau de contrôle était médiocre, et pourtant, ilstringstream
devrait être utilisé à la place delexical_cast
si vous avez besoin d'un "niveau de contrôle supérieur". De plus, comme il nelexical_cast
s'agit que d'un wrapperstringstream
, il souffre des mêmes problèmes que celui-stringstream
ci: mauvaise prise en charge de plusieurs bases de nombres et mauvaise gestion des erreurs.La meilleure solution
Heureusement, quelqu'un a déjà résolu tous les problèmes ci-dessus. La bibliothèque standard C contient une
strtol
famille qui n'a aucun de ces problèmes.Assez simple pour quelque chose qui gère tous les cas d'erreur et prend également en charge n'importe quelle base numérique de 2 à 36. Si
base
est zéro (la valeur par défaut), il essaiera de convertir à partir de n'importe quelle base. Ou l'appelant peut fournir le troisième argument et spécifier que la conversion ne doit être tentée que pour une base particulière. Il est robuste et gère toutes les erreurs avec un minimum d'effort.Autres raisons de préférer
strtol
(et la famille):Il n'y a absolument aucune bonne raison d'utiliser une autre méthode.
la source
strtol
être thread-safe. POSIX nécessite égalementerrno
d'utiliser le stockage local des threads. Même sur les systèmes non POSIX, presque toutes les implémentations deerrno
sur les systèmes multithread utilisent le stockage thread-local. La dernière norme C ++ doiterrno
être compatible POSIX. La dernière norme C requiert également unerrno
stockage local par thread. Même sous Windows, qui n'est certainement pas conforme à POSIX,errno
est thread-safe et, par extension, l'est égalementstrtol
.std::stol
pour cela, qui générera de manière appropriée des exceptions plutôt que de renvoyer des constantes.std::stol
même d'être ajoutée au langage C ++. Cela dit, je ne pense pas qu'il soit juste de dire qu'il s'agit de "codage C en C ++". C'est idiot de dire questd::strtol
c'est du codage C quand il fait explicitement partie du langage C ++. Ma réponse s'appliquait parfaitement au C ++ quand il a été écrit et il s'applique toujours même avec le nouveaustd::stol
. L'appel de fonctions qui peuvent lever des exceptions n'est pas toujours le meilleur pour chaque situation de programmation.C'est un moyen C plus sûr qu'atoi ()
C ++ avec chaîne de chaînes de bibliothèque standard : (merci CMS )
Avec la bibliothèque boost : (merci jk )
Modifier: correction de la version de chaîne de caractères afin qu'elle gère les erreurs. (merci aux commentaires de CMS et de jk sur le post original)
la source
La bonne vieille façon C fonctionne toujours. Je recommande strtol ou strtoul. Entre l'état de retour et le «endPtr», vous pouvez donner une bonne sortie de diagnostic. Il gère également très bien plusieurs bases.
la source
Vous pouvez utiliser Boost
lexical_cast
, qui encapsule cela dans une interface plus générique.lexical_cast<Target>(Source)
jettebad_lexical_cast
sur l'échec.la source
Vous pouvez utiliser le flux de chaînes a du libraray standard C ++:
Voir les pièges du flux pour les pièges de la gestion des erreurs et des flux en C ++.
la source
Vous pouvez utiliser le stringstream
la source
Je pense que ces trois liens résument:
Les solutions stringstream et lexical_cast sont à peu près les mêmes que le cast lexical utilise stringstream.
Certaines spécialisations de la fonte lexicale utilisent une approche différente, voir http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp pour plus de détails. Les entiers et les flottants sont désormais spécialisés pour la conversion d'entiers en chaînes.
On peut spécialiser lexical_cast pour ses propres besoins et faire vite. Ce serait la solution ultime satisfaisant toutes les parties, propre et simple.
Les articles déjà mentionnés montrent une comparaison entre différentes méthodes de conversion d'entiers <-> chaînes. Les approches suivantes ont du sens: ancienne voie C, spirit.karma, format rapide, boucle naïve simple.
Lexical_cast est correct dans certains cas, par exemple pour la conversion int en chaîne.
La conversion de chaîne en entier à l'aide d'une conversion lexicale n'est pas une bonne idée car elle est 10 à 40 fois plus lente qu'atoi selon la plate-forme / le compilateur utilisé.
Boost.Spirit.Karma semble être la bibliothèque la plus rapide pour convertir un entier en chaîne.
et la boucle simple de base de l'article mentionné ci-dessus est un moyen le plus rapide de convertir une chaîne en int, évidemment pas la plus sûre, strtol () semble être une solution plus sûre
la source
La bibliothèque C ++ String Toolkit (StrTk) a la solution suivante:
InputIterator peut être soit des itérateurs char *, char * ou std :: string non signés, et T devrait être un int signé, tel que int signé, int ou long
la source
v = (10 * v) + digit;
déborde inutilement avec une entrée de chaîne avec la valeur texte deINT_MIN
. Le tableau est d'une valeur discutable vs simplementdigit >= '0' && digit <= '9'
Si vous avez C ++ 11, les solutions appropriées sont aujourd'hui le C ++ entier des fonctions de conversion en
<string>
:stoi
,stol
,stoul
,stoll
,stoull
. Ils lancent des exceptions appropriées lorsqu'ils reçoivent une entrée incorrecte et utilisent les fonctions rapides et petitesstrto*
sous le capot.Si vous êtes bloqué avec une révision antérieure de C ++, il serait transférable de vous d'imiter ces fonctions dans votre implémentation.
la source
À partir de C ++ 17, vous pouvez utiliser à
std::from_chars
partir de l'en-<charconv>
tête comme indiqué ici .Par exemple:
En prime, il peut également gérer d'autres bases, comme hexadécimales.
la source
J'aime la réponse de Dan Moulding , je vais juste y ajouter un peu de style C ++:
Il fonctionne à la fois pour std :: string et const char * via la conversion implicite. Il est également utile pour la conversion de base, par exemple tous
to_int("0x7b")
etto_int("0173")
etto_int("01111011", 2)
etto_int("0000007B", 16)
etto_int("11120", 3)
etto_int("3L", 34);
retournerait 123.Contrairement à
std::stoi
cela fonctionne dans pré-C ++ 11. Contrairement àstd::stoi
,boost::lexical_cast
etstringstream
il lève des exceptions pour les chaînes étranges comme "123hohoho".NB: Cette fonction tolère les espaces de début mais pas les espaces de fin, c'est-à-dire
to_int(" 123")
renvoie 123 tout ento_int("123 ")
lançant une exception. Assurez-vous que cela est acceptable pour votre cas d'utilisation ou ajustez le code.Une telle fonction pourrait faire partie de STL ...
la source
Je connais trois façons de convertir String en int:
Soit utiliser la fonction stoi (String to int) ou tout simplement aller avec Stringstream, la troisième façon de procéder à une conversion individuelle, le code est ci-dessous:
1ère méthode
2ème méthode
3e méthode - mais pas pour une conversion individuelle
la source
J'aime la réponse de Dan , en particulier parce qu'il évite les exceptions. Pour le développement de systèmes embarqués et autres développements de systèmes de bas niveau, il n’existe peut-être pas de structure d’exception appropriée.
Ajout d'une vérification d'espace blanc après une chaîne valide ... ces trois lignes
Ajout d'une vérification des erreurs d'analyse également.
Voici la fonction complète ..
la source
" "
.strtol()
n'est pas spécifié à définirerrno
lorsqu'aucune conversion ne se produit. Mieux vaut utiliserif (s == end) return INCONVERTIBLE;
pour détecter aucune conversion. Et puisif (*s == '\0' || *end != '\0')
peut se simplifier enif (*end)
2)|| l > LONG_MAX
et|| l < LONG_MIN
ne servir à rien - ils ne sont jamais vrais.Vous pouvez utiliser cette méthode définie.
Et si vous deviez convertir une chaîne en entier, vous feriez simplement ce qui suit.
La sortie serait 102.
la source
atoi
ne semble pas être "la manière C ++", à la lumière d'autres réponses comme acceptéestd::stoi()
.Je sais que c'est une question plus ancienne, mais je l'ai rencontrée tant de fois et, à ce jour, je n'ai toujours pas trouvé de solution bien conçue ayant les caractéristiques suivantes:
Alors, voici le mien, avec une sangle de test. Parce qu'il utilise les fonctions C strtoull / strtoll sous le capot, il se convertit toujours d'abord en le plus grand type disponible. Ensuite, si vous n'utilisez pas le plus grand type, il effectuera des vérifications de plage supplémentaires pour vérifier que votre type n'était pas sur (sous) débordé. Pour cela, il est un peu moins performant que si l'on choisit correctement strtol / strtoul. Cependant, cela fonctionne également pour les courts métrages / caractères et, à ma connaissance, il n'existe pas de fonction de bibliothèque standard qui le fasse également.
Prendre plaisir; j'espère que quelqu'un le trouvera utile.
StringToDecimal
est la méthode de l'utilisateur-terre; il est surchargé, il peut donc être appelé comme ceci:ou ca:
Je déteste répéter le type int, alors préférez ce dernier. Cela garantit que si le type de «a» change, on n'obtient pas de mauvais résultats. Je souhaite que le compilateur puisse le comprendre comme:
... mais, C ++ ne déduit pas les types de retour de modèle, c'est donc le meilleur que je puisse obtenir.
L'implémentation est assez simple:
CstrtoxllWrapper
encapsule les deuxstrtoull
etstrtoll
, en appelant ce qui est nécessaire en fonction de la signature du type de modèle et en fournissant des garanties supplémentaires (par exemple, une entrée négative est interdite si non signée et elle garantit que la chaîne entière a été convertie).CstrtoxllWrapper
est utilisé parStringToSigned
etStringToUnsigned
avec le plus grand type (long long / unsigned long long) disponible pour le compilateur; cela permet d'effectuer la conversion maximale. Ensuite, si cela est nécessaire,StringToSigned
/StringToUnsigned
effectue les dernières vérifications de plage sur le type sous-jacent. Enfin, la méthode du point de terminaisonStringToDecimal
,, décide des méthodes de modèle StringTo * à appeler en fonction de la signature du type sous-jacent.Je pense que la plupart des fichiers indésirables peuvent être optimisés par le compilateur; à peu près tout devrait être déterministe au moment de la compilation. Tout commentaire sur cet aspect me serait intéressant!
la source
long long
au lieu deintmax_t
?if (ePtr != str)
. En outre, utilisezisspace((unsigned char) *ePtr)
pour gérer correctement les valeurs négatives de*ePtr
.En C, vous pouvez utiliser
int atoi (const char * str)
,Analyse la chaîne C en interprétant son contenu comme un nombre entier, qui est renvoyé comme une valeur de type int.
la source
atoi
dans la question, j'en suis conscient. La question n'est clairement pas sur C, mais sur C ++. -1