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 ).
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.
int myNr = std::stoi(myString);
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
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, mais pos
sera 2 au lieu de la longueur de chaîne 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
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:
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
Cela a un problème majeur: str2int(i, "1337h4x0r")
reviendra avec plaisir true
et i
obtiendra la valeur 1337
. Nous pouvons contourner ce problème en nous assurant qu'il n'y a plus de caractères stringstream
après la conversion:
bool str2int (int &i, char const *s)
{
char c;
std::stringstream ss(s);
ss >> i;
if (ss.fail() || ss.get(c)) {
// not an integer
return false;
}
return true;
}
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 bits int
)? Avec stringstream
, 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 la lexical_cast
documentation a à dire:
Lorsqu'un degré de contrôle plus élevé est requis sur les conversions, std :: stringstream et std :: wstringstream offrent un chemin plus approprié. Lorsque des conversions non basées sur des flux sont requises, lexical_cast n'est pas le bon outil pour le travail et n'est pas spécialement conçu pour de tels scénarios.
Quoi?? Nous avons déjà vu que le stringstream
niveau de contrôle était médiocre, et pourtant, il stringstream
devrait être utilisé à la place de lexical_cast
si vous avez besoin d'un "niveau de contrôle supérieur". De plus, comme il ne lexical_cast
s'agit que d'un wrapper stringstream
, 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.
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.
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
char *end;
long l;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
return OVERFLOW;
}
if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
return UNDERFLOW;
}
if (*s == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
i = l;
return SUCCESS;
}
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.
strtol
être thread-safe. POSIX nécessite également errno
d'utiliser le stockage local des threads. Même sur les systèmes non POSIX, presque toutes les implémentations de errno
sur les systèmes multithread utilisent le stockage thread-local. La dernière norme C ++ doit errno
être compatible POSIX. La dernière norme C requiert également un errno
stockage local par thread. Même sous Windows, qui n'est certainement pas conforme à POSIX, errno
est thread-safe et, par extension, l'est également strtol
.
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 que std::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 nouveau std::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 ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ avec chaîne de chaînes de bibliothèque standard : (merci CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
Avec la bibliothèque boost : (merci jk )
#include <boost/lexical_cast.hpp>
#include <string>
try
{
std::string str = "123";
int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
// Error
}
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 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.
Vous pouvez utiliser Boostlexical_cast
, qui encapsule cela dans une interface plus générique.
lexical_cast<Target>(Source)
jette bad_lexical_cast
sur l'échec.
Vous pouvez utiliser le flux de chaînes a du libraray standard C ++:
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
L'état du flux sera défini pour échouer si un non-chiffre est rencontré lors de la tentative de lecture d'un entier.
Voir les pièges du flux pour les pièges de la gestion des erreurs et des flux en C ++.
Vous pouvez utiliser le stringstream
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
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.
ex.: generate(ptr_char, int_, integer_number);
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
int naive_char_2_int(const char *p) {
int x = 0;
bool neg = false;
if (*p == '-') {
neg = true;
++p;
}
while (*p >= '0' && *p <= '9') {
x = (x*10) + (*p - '0');
++p;
}
if (neg) {
x = -x;
}
return x;
}
La bibliothèque C ++ String Toolkit (StrTk) a la solution suivante:
static const std::size_t digit_table_symbol_count = 256;
static const unsigned char digit_table[digit_table_symbol_count] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF - 0x07
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 - 0x0F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 - 0x17
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 - 0x1F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 - 0x27
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x28 - 0x2F
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x30 - 0x37
0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 - 0x3F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 - 0x47
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x48 - 0x4F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 - 0x57
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 - 0x5F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 - 0x67
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x68 - 0x6F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x70 - 0x77
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 - 0x7F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x80 - 0x87
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x88 - 0x8F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x90 - 0x97
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x98 - 0x9F
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA0 - 0xA7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xA8 - 0xAF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB0 - 0xB7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xB8 - 0xBF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC0 - 0xC7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xC8 - 0xCF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD0 - 0xD7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xD8 - 0xDF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 - 0xE7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE8 - 0xEF
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xF0 - 0xF7
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 0xF8 - 0xFF
};
template<typename InputIterator, typename T>
inline bool string_to_signed_type_converter_impl_itr(InputIterator begin, InputIterator end, T& v)
{
if (0 == std::distance(begin,end))
return false;
v = 0;
InputIterator it = begin;
bool negative = false;
if ('+' == *it)
++it;
else if ('-' == *it)
{
++it;
negative = true;
}
if (end == it)
return false;
while(end != it)
{
const T digit = static_cast<T>(digit_table[static_cast<unsigned int>(*it++)]);
if (0xFF == digit)
return false;
v = (10 * v) + digit;
}
if (negative)
v *= -1;
return true;
}
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
v = (10 * v) + digit;
déborde inutilement avec une entrée de chaîne avec la valeur texte de INT_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 petites strto*
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.
À partir de C ++ 17, vous pouvez utiliser à std::from_chars
partir de l'en- <charconv>
tête comme indiqué ici .
Par exemple:
#include <iostream>
#include <charconv>
#include <array>
int main()
{
char const * str = "42";
int value = 0;
std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);
if(result.error == std::errc::invalid_argument)
{
std::cout << "Error, invalid format";
}
else if(result.error == std::errc::result_out_of_range)
{
std::cout << "Error, value too big for int range";
}
else
{
std::cout << "Success: " << result;
}
}
En prime, il peut également gérer d'autres bases, comme hexadécimales.
J'aime la réponse de Dan Moulding , je vais juste y ajouter un peu de style C ++:
#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>
int to_int(const std::string &s, int base = 0)
{
char *end;
errno = 0;
long result = std::strtol(s.c_str(), &end, base);
if (errno == ERANGE || result > INT_MAX || result < INT_MIN)
throw std::out_of_range("toint: string is out of range");
if (s.length() == 0 || *end != '\0')
throw std::invalid_argument("toint: invalid string");
return result;
}
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")
et to_int("0173")
et to_int("01111011", 2)
et to_int("0000007B", 16)
et to_int("11120", 3)
et to_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 en to_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 ...
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
std::string s1 = "4533";
std::string s2 = "3.010101";
std::string s3 = "31337 with some string";
int myint1 = std::stoi(s1);
int myint2 = std::stoi(s2);
int myint3 = std::stoi(s3);
std::cout << s1 <<"=" << myint1 << '\n';
std::cout << s2 <<"=" << myint2 << '\n';
std::cout << s3 <<"=" << myint3 << '\n';
2ème méthode
#include <string.h>
#include <sstream>
#include <iostream>
#include <cstring>
using namespace std;
int StringToInteger(string NumberAsString)
{
int NumberAsInteger;
stringstream ss;
ss << NumberAsString;
ss >> NumberAsInteger;
return NumberAsInteger;
}
int main()
{
string NumberAsString;
cin >> NumberAsString;
cout << StringToInteger(NumberAsString) << endl;
return 0;
}
3e méthode - mais pas pour une conversion individuelle
std::string str4 = "453";
int i = 0, in=0; // 453 as on
for ( i = 0; i < str4.length(); i++)
{
in = str4[i];
cout <<in-48 ;
}
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
while (isspace(*end)) {
end++;
}
Ajout d'une vérification des erreurs d'analyse également.
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
Voici la fonction complète ..
#include <cstdlib>
#include <cerrno>
#include <climits>
#include <stdexcept>
enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };
STR2INT_ERROR str2long (long &l, char const *s, int base = 0)
{
char *end = (char *)s;
errno = 0;
l = strtol(s, &end, base);
if ((errno == ERANGE) && (l == LONG_MAX)) {
return OVERFLOW;
}
if ((errno == ERANGE) && (l == LONG_MIN)) {
return UNDERFLOW;
}
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
while (isspace((unsigned char)*end)) {
end++;
}
if (*s == '\0' || *end != '\0') {
return INCONVERTIBLE;
}
return SUCCESS;
}
" "
. strtol()
n'est pas spécifié à définir errno
lorsqu'aucune conversion ne se produit. Mieux vaut utiliser if (s == end) return INCONVERTIBLE;
pour détecter aucune conversion. Et puis if (*s == '\0' || *end != '\0')
peut se simplifier en if (*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.
#define toInt(x) {atoi(x.c_str())};
Et si vous deviez convertir une chaîne en entier, vous feriez simplement ce qui suit.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
La sortie serait 102.
atoi
ne semble pas être "la manière C ++", à la lumière d'autres réponses comme acceptée std::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.
#include <cstdlib>
#include <cerrno>
#include <limits>
#include <stdexcept>
#include <sstream>
static const int DefaultBase = 10;
template<typename T>
static inline T CstrtoxllWrapper(const char *str, int base = DefaultBase)
{
while (isspace(*str)) str++; // remove leading spaces; verify there's data
if (*str == '\0') { throw std::invalid_argument("str; no data"); } // nothing to convert
// NOTE: for some reason strtoull allows a negative sign, we don't; if
// converting to an unsigned then it must always be positive!
if (!std::numeric_limits<T>::is_signed && *str == '-')
{ throw std::invalid_argument("str; negative"); }
// reset errno and call fn (either strtoll or strtoull)
errno = 0;
char *ePtr;
T tmp = std::numeric_limits<T>::is_signed ? strtoll(str, &ePtr, base)
: strtoull(str, &ePtr, base);
// check for any C errors -- note these are range errors on T, which may
// still be out of the range of the actual type we're using; the caller
// may need to perform additional range checks.
if (errno != 0)
{
if (errno == ERANGE) { throw std::range_error("str; out of range"); }
else if (errno == EINVAL) { throw std::invalid_argument("str; EINVAL"); }
else { throw std::invalid_argument("str; unknown errno"); }
}
// verify everything converted -- extraneous spaces are allowed
if (ePtr != NULL)
{
while (isspace(*ePtr)) ePtr++;
if (*ePtr != '\0') { throw std::invalid_argument("str; bad data"); }
}
return tmp;
}
template<typename T>
T StringToSigned(const char *str, int base = DefaultBase)
{
static const long long max = std::numeric_limits<T>::max();
static const long long min = std::numeric_limits<T>::min();
long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type
// final range check -- only needed if not long long type; a smart compiler
// should optimize this whole thing out
if (sizeof(T) == sizeof(tmp)) { return tmp; }
if (tmp < min || tmp > max)
{
std::ostringstream err;
err << "str; value " << tmp << " out of " << sizeof(T) * 8
<< "-bit signed range (";
if (sizeof(T) != 1) err << min << ".." << max;
else err << (int) min << ".." << (int) max; // don't print garbage chars
err << ")";
throw std::range_error(err.str());
}
return tmp;
}
template<typename T>
T StringToUnsigned(const char *str, int base = DefaultBase)
{
static const unsigned long long max = std::numeric_limits<T>::max();
unsigned long long tmp = CstrtoxllWrapper<typeof(tmp)>(str, base); // use largest type
// final range check -- only needed if not long long type; a smart compiler
// should optimize this whole thing out
if (sizeof(T) == sizeof(tmp)) { return tmp; }
if (tmp > max)
{
std::ostringstream err;
err << "str; value " << tmp << " out of " << sizeof(T) * 8
<< "-bit unsigned range (0..";
if (sizeof(T) != 1) err << max;
else err << (int) max; // don't print garbage chars
err << ")";
throw std::range_error(err.str());
}
return tmp;
}
template<typename T>
inline T
StringToDecimal(const char *str, int base = DefaultBase)
{
return std::numeric_limits<T>::is_signed ? StringToSigned<T>(str, base)
: StringToUnsigned<T>(str, base);
}
template<typename T>
inline T
StringToDecimal(T &out_convertedVal, const char *str, int base = DefaultBase)
{
return out_convertedVal = StringToDecimal<T>(str, base);
}
/*============================== [ Test Strap ] ==============================*/
#include <inttypes.h>
#include <iostream>
static bool _g_anyFailed = false;
template<typename T>
void TestIt(const char *tName,
const char *s, int base,
bool successExpected = false, T expectedValue = 0)
{
#define FAIL(s) { _g_anyFailed = true; std::cout << s; }
T x;
std::cout << "converting<" << tName << ">b:" << base << " [" << s << "]";
try
{
StringToDecimal<T>(x, s, base);
// get here on success only
if (!successExpected)
{
FAIL(" -- TEST FAILED; SUCCESS NOT EXPECTED!" << std::endl);
}
else
{
std::cout << " -> ";
if (sizeof(T) != 1) std::cout << x;
else std::cout << (int) x; // don't print garbage chars
if (x != expectedValue)
{
FAIL("; FAILED (expected value:" << expectedValue << ")!");
}
std::cout << std::endl;
}
}
catch (std::exception &e)
{
if (successExpected)
{
FAIL( " -- TEST FAILED; EXPECTED SUCCESS!"
<< " (got:" << e.what() << ")" << std::endl);
}
else
{
std::cout << "; expected exception encounterd: [" << e.what() << "]" << std::endl;
}
}
}
#define TEST(t, s, ...) \
TestIt<t>(#t, s, __VA_ARGS__);
int main()
{
std::cout << "============ variable base tests ============" << std::endl;
TEST(int, "-0xF", 0, true, -0xF);
TEST(int, "+0xF", 0, true, 0xF);
TEST(int, "0xF", 0, true, 0xF);
TEST(int, "-010", 0, true, -010);
TEST(int, "+010", 0, true, 010);
TEST(int, "010", 0, true, 010);
TEST(int, "-10", 0, true, -10);
TEST(int, "+10", 0, true, 10);
TEST(int, "10", 0, true, 10);
std::cout << "============ base-10 tests ============" << std::endl;
TEST(int, "-010", 10, true, -10);
TEST(int, "+010", 10, true, 10);
TEST(int, "010", 10, true, 10);
TEST(int, "-10", 10, true, -10);
TEST(int, "+10", 10, true, 10);
TEST(int, "10", 10, true, 10);
TEST(int, "00010", 10, true, 10);
std::cout << "============ base-8 tests ============" << std::endl;
TEST(int, "777", 8, true, 0777);
TEST(int, "-0111 ", 8, true, -0111);
TEST(int, "+0010 ", 8, true, 010);
std::cout << "============ base-16 tests ============" << std::endl;
TEST(int, "DEAD", 16, true, 0xDEAD);
TEST(int, "-BEEF", 16, true, -0xBEEF);
TEST(int, "+C30", 16, true, 0xC30);
std::cout << "============ base-2 tests ============" << std::endl;
TEST(int, "-10011001", 2, true, -153);
TEST(int, "10011001", 2, true, 153);
std::cout << "============ irregular base tests ============" << std::endl;
TEST(int, "Z", 36, true, 35);
TEST(int, "ZZTOP", 36, true, 60457993);
TEST(int, "G", 17, true, 16);
TEST(int, "H", 17);
std::cout << "============ space deliminated tests ============" << std::endl;
TEST(int, "1337 ", 10, true, 1337);
TEST(int, " FEAD", 16, true, 0xFEAD);
TEST(int, " 0711 ", 0, true, 0711);
std::cout << "============ bad data tests ============" << std::endl;
TEST(int, "FEAD", 10);
TEST(int, "1234 asdfklj", 10);
TEST(int, "-0xF", 10);
TEST(int, "+0xF", 10);
TEST(int, "0xF", 10);
TEST(int, "-F", 10);
TEST(int, "+F", 10);
TEST(int, "12.4", 10);
TEST(int, "ABG", 16);
TEST(int, "10011002", 2);
std::cout << "============ int8_t range tests ============" << std::endl;
TEST(int8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
TEST(int8_t, "80", 16);
TEST(int8_t, "-80", 16, true, std::numeric_limits<int8_t>::min());
TEST(int8_t, "-81", 16);
TEST(int8_t, "FF", 16);
TEST(int8_t, "100", 16);
std::cout << "============ uint8_t range tests ============" << std::endl;
TEST(uint8_t, "7F", 16, true, std::numeric_limits<int8_t>::max());
TEST(uint8_t, "80", 16, true, std::numeric_limits<int8_t>::max()+1);
TEST(uint8_t, "-80", 16);
TEST(uint8_t, "-81", 16);
TEST(uint8_t, "FF", 16, true, std::numeric_limits<uint8_t>::max());
TEST(uint8_t, "100", 16);
std::cout << "============ int16_t range tests ============" << std::endl;
TEST(int16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
TEST(int16_t, "8000", 16);
TEST(int16_t, "-8000", 16, true, std::numeric_limits<int16_t>::min());
TEST(int16_t, "-8001", 16);
TEST(int16_t, "FFFF", 16);
TEST(int16_t, "10000", 16);
std::cout << "============ uint16_t range tests ============" << std::endl;
TEST(uint16_t, "7FFF", 16, true, std::numeric_limits<int16_t>::max());
TEST(uint16_t, "8000", 16, true, std::numeric_limits<int16_t>::max()+1);
TEST(uint16_t, "-8000", 16);
TEST(uint16_t, "-8001", 16);
TEST(uint16_t, "FFFF", 16, true, std::numeric_limits<uint16_t>::max());
TEST(uint16_t, "10000", 16);
std::cout << "============ int32_t range tests ============" << std::endl;
TEST(int32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
TEST(int32_t, "80000000", 16);
TEST(int32_t, "-80000000", 16, true, std::numeric_limits<int32_t>::min());
TEST(int32_t, "-80000001", 16);
TEST(int32_t, "FFFFFFFF", 16);
TEST(int32_t, "100000000", 16);
std::cout << "============ uint32_t range tests ============" << std::endl;
TEST(uint32_t, "7FFFFFFF", 16, true, std::numeric_limits<int32_t>::max());
TEST(uint32_t, "80000000", 16, true, std::numeric_limits<int32_t>::max()+1);
TEST(uint32_t, "-80000000", 16);
TEST(uint32_t, "-80000001", 16);
TEST(uint32_t, "FFFFFFFF", 16, true, std::numeric_limits<uint32_t>::max());
TEST(uint32_t, "100000000", 16);
std::cout << "============ int64_t range tests ============" << std::endl;
TEST(int64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
TEST(int64_t, "8000000000000000", 16);
TEST(int64_t, "-8000000000000000", 16, true, std::numeric_limits<int64_t>::min());
TEST(int64_t, "-8000000000000001", 16);
TEST(int64_t, "FFFFFFFFFFFFFFFF", 16);
TEST(int64_t, "10000000000000000", 16);
std::cout << "============ uint64_t range tests ============" << std::endl;
TEST(uint64_t, "7FFFFFFFFFFFFFFF", 16, true, std::numeric_limits<int64_t>::max());
TEST(uint64_t, "8000000000000000", 16, true, std::numeric_limits<int64_t>::max()+1);
TEST(uint64_t, "-8000000000000000", 16);
TEST(uint64_t, "-8000000000000001", 16);
TEST(uint64_t, "FFFFFFFFFFFFFFFF", 16, true, std::numeric_limits<uint64_t>::max());
TEST(uint64_t, "10000000000000000", 16);
std::cout << std::endl << std::endl
<< (_g_anyFailed ? "!! SOME TESTS FAILED !!" : "ALL TESTS PASSED")
<< std::endl;
return _g_anyFailed;
}
StringToDecimal
est la méthode de l'utilisateur-terre; il est surchargé, il peut donc être appelé comme ceci:
int a; a = StringToDecimal<int>("100");
ou ca:
int a; StringToDecimal(a, "100");
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:
int a; a = StringToDecimal("100");
... 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 deux strtoull
et strtoll
, 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é par StringToSigned
et StringToUnsigned
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 terminaison StringToDecimal
,, 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!
long long
au lieu de intmax_t
?
if (ePtr != str)
. En outre, utilisez isspace((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.
atoi
dans la question, j'en suis conscient. La question n'est clairement pas sur C, mais sur C ++. -1