J'ai eu une belle conversation avec quelqu'un sur les chutes de std::stoi
. Pour le dire franchement, il utilise std::strtol
En interne, et lance si cela signale une erreur. Selon eux, cependant, std::strtol
Ne devrait pas signaler une erreur pour une entrée de "abcxyz"
, Empêchant stoi
de lancer std::invalid_argument
.
Tout d'abord, voici deux programmes testés sur GCC sur les comportements de ces cas:
strtol
stoi
Les deux affichent un succès sur "123"
Et un échec sur "abc"
.
J'ai regardé dans la norme pour tirer plus d'informations:
§ 21.5
Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that
no conversion could be performed. Throws out_of_range if the converted value is
outside the range of representable values for the return type.
Cela résume le comportement de s'appuyer sur strtol
. Qu'en est-il maintenant de strtol
? J'ai trouvé cela dans le projet C11:
§7.22.1.4
If the subject sequence is empty or does not have the expected form, no
conversion is performed; the value of nptr is stored in the object
pointed to by endptr, provided that endptr is not a null pointer.
Étant donné la situation de la transmission de "abc"
, La norme C stipule que nptr
, qui pointe vers le début de la chaîne, serait stocké dans endptr
, le pointeur transmis. Cela semble cohérent avec le test. De plus, 0 doit être retourné, comme indiqué par ceci:
§7.22.1.4
If no conversion could be performed, zero is returned.
La référence précédente indiquait qu'aucune conversion ne serait effectuée, elle doit donc retourner 0. Ces conditions sont désormais conformes à la norme C++ 11 pour stoi
lancer std::invalid_argument
.
Le résultat de cela est important pour moi parce que je ne veux pas recommander stoi
comme une meilleure alternative aux autres méthodes de conversion de chaîne en int, ou l'utiliser moi-même comme si cela fonctionnait comme vous l'attendiez , s'il ne capture pas le texte comme une conversion non valide.
Donc, après tout cela, je me suis trompé quelque part? Il me semble que j'ai une bonne preuve de la levée de cette exception. Ma preuve est-elle valide, ou std::stoi
N'est-il pas garanti de lever cette exception lorsqu'elle reçoit "abc"
?
std::stoi
Génère-t-il une erreur sur l'entrée "abcxyz"
?Oui.
Je pense que votre confusion peut venir du fait que strtol
ne rapporte jamais ne erreur sauf en cas de débordement. Il peut signaler qu'aucune conversion n'a été effectuée, mais cela n'est jamais considéré comme une condition d'erreur dans la norme C.
strtol
est défini de manière similaire par les trois normes C, et je vous épargnerai les détails ennuyeux, mais il définit essentiellement une "séquence de sujet" qui est une sous-chaîne de la chaîne d'entrée correspondant au nombre réel. Les quatre conditions suivantes sont équivalentes:
*endptr != nptr
(Cela n'a de sens que lorsque endptr
n'est pas nul)En cas de débordement, la conversion est toujours supposée avoir eu lieu.
Maintenant, il est tout à fait clair que parce que "abcxyz"
Ne contient pas de nombre, la séquence objet de la chaîne "abcxyz"
Doit être vide, afin qu'aucune conversion ne puisse être effectuée. Le programme C90/C99/C11 suivant le confirmera expérimentalement:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *nptr = "abcxyz", *endptr[1];
strtol(nptr, endptr, 0);
if (*endptr == nptr)
printf("No conversion could be performed.\n");
return 0;
}
Cela implique que toute implémentation conforme de std::stoi
doit lancer invalid_argument
Lorsqu'elle reçoit l'entrée "abcxyz"
Sans argument de base facultatif.
std::stoi
A une vérification d'erreur satisfaisante?Non. La personne à qui vous avez parlé a raison lorsqu'elle dit que std::stoi
Est plus indulgent que d'effectuer la vérification complète errno == 0 && end != start && *end=='\0'
Après std::strtol
, Parce que std::stoi
En silence supprime tous les caractères à partir du premier caractère non numérique de la chaîne.
En fait, du haut de ma tête, la seule langue dont la conversion native se comporte un peu comme std::stoi
Est Javascript, et même dans ce cas, vous devez forcer la base 10 avec parseInt(n, 10)
pour éviter le cas particulier de l'hexadécimal Nombres:
input | std::atoi std::stoi Javascript full check
===========+=============================================================
hello | 0 error error(NaN) error
0xygen | 0 0 error(NaN) error
0x42 | 0 0 66 error
42x0 | 42 42 42 error
42 | 42 42 42 42
-----------+-------------------------------------------------------------
languages | Perl, Ruby, Javascript Javascript C#, Java,
| PHP, C... (base 10) Python...
Remarque: il existe également des différences entre les langues dans la gestion des espaces et des signes + redondants.
Je ne connais aucune fonction intégrée qui fasse cela, mais boost::lexical_cast<int>
Fera ce que vous voulez. Il est particulièrement strict car il rejette même les espaces environnants, contrairement à la fonction int()
de Python. Notez que les caractères non valides et les débordements entraînent la même exception, boost::bad_lexical_cast
.
#include <boost/lexical_cast.hpp>
int main() {
std::string s = "42";
try {
int n = boost::lexical_cast<int>(s);
std::cout << "n = " << n << std::endl;
} catch (boost::bad_lexical_cast) {
std::cout << "conversion failed" << std::endl;
}
}