Apa cara C ++ mem-parsing string (diberikan sebagai char *) ke dalam int? Penanganan kesalahan yang kuat dan jelas merupakan nilai tambah (bukan mengembalikan nol ).
Apa cara C ++ mem-parsing string (diberikan sebagai char *) ke dalam int? Penanganan kesalahan yang kuat dan jelas merupakan nilai tambah (bukan mengembalikan nol ).
Jawaban:
Di C ++ 11 baru ada fungsi untuk itu: stoi, stol, stoll, stoul dan sebagainya.
int myNr = std::stoi(myString);
Ini akan menimbulkan pengecualian pada kesalahan konversi.
Bahkan fungsi-fungsi baru ini masih memiliki masalah yang sama seperti dicatat oleh Dan: mereka dengan senang hati akan mengubah string "11x" menjadi integer "11".
Lihat lebih lanjut: http://en.cppreference.com/w/cpp/string/basic_string/stol
size_t
tidak sama dengan panjang string, maka berhenti lebih awal. Itu masih akan mengembalikan 11 dalam kasus itu, tetapi pos
akan menjadi 2 bukannya panjang string 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Ini saran pertama saya: jangan gunakan stringstream untuk ini . Meskipun pada awalnya mungkin terlihat mudah digunakan, Anda akan menemukan bahwa Anda harus melakukan banyak pekerjaan tambahan jika Anda ingin ketahanan dan penanganan kesalahan yang baik.
Berikut ini adalah pendekatan yang secara intuitif tampaknya berhasil:
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
Ini memiliki masalah besar: str2int(i, "1337h4x0r")
dengan senang hati akan kembali true
dan i
akan mendapatkan nilai 1337
. Kami dapat mengatasi masalah ini dengan memastikan tidak ada lagi karakter dalam stringstream
setelah konversi:
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;
}
Kami memperbaiki satu masalah, tetapi masih ada beberapa masalah lainnya.
Bagaimana jika nomor dalam string bukan basis 10? Kami dapat mencoba mengakomodasi basis lain dengan mengatur aliran ke mode yang benar (misalnya ss << std::hex
) sebelum mencoba konversi. Tapi ini berarti penelepon harus tahu apriori apa dasar nomor itu - dan bagaimana penelepon itu bisa tahu itu? Penelepon belum tahu nomornya. Mereka bahkan tidak tahu bahwa itu adalahsebuah angka! Bagaimana mereka bisa tahu dasar apa itu? Kami hanya bisa mengamanatkan bahwa semua input angka ke program kami harus basis 10 dan menolak input heksadesimal atau oktal sebagai tidak valid. Tapi itu tidak terlalu fleksibel atau kuat. Tidak ada solusi sederhana untuk masalah ini. Anda tidak dapat hanya mencoba konversi satu kali untuk setiap basis, karena konversi desimal akan selalu berhasil untuk angka oktal (dengan nol di depan) dan konversi oktal dapat berhasil untuk beberapa angka desimal. Jadi sekarang Anda harus memeriksa nol di depan. Tapi tunggu! Angka heksadesimal dapat dimulai dengan nol di depannya juga (0x ...). Mendesah.
Bahkan jika Anda berhasil menangani masalah di atas, masih ada masalah lain yang lebih besar: bagaimana jika penelepon perlu membedakan antara input yang buruk (mis. "123foo") dan angka yang berada di luar kisaran int
(mis. "4000000000" untuk 32-bit int
)? Dengan stringstream
, tidak ada cara untuk membuat perbedaan ini. Kami hanya tahu apakah konversi berhasil atau gagal. Jika gagal, kami tidak tahu mengapa itu gagal. Seperti yang Anda lihat, stringstream
banyak yang harus diinginkan jika Anda ingin ketahanan dan penanganan kesalahan yang jelas.
Ini menuntun saya ke saran kedua: jangan gunakan Boost lexical_cast
untuk ini . Pertimbangkan apa yang dikatakan oleh lexical_cast
dokumentasi:
Di mana diperlukan tingkat kontrol yang lebih tinggi atas konversi, std :: stringstream dan std :: wstringstream menawarkan jalur yang lebih tepat. Di mana konversi non-stream diperlukan, lexical_cast adalah alat yang salah untuk pekerjaan itu dan tidak dikhususkan untuk skenario seperti itu.
Apa?? Kami telah melihat bahwa stringstream
memiliki tingkat kontrol yang buruk, namun dikatakan stringstream
harus digunakan alih-alih lexical_cast
jika Anda memerlukan "tingkat kontrol yang lebih tinggi". Juga, karena lexical_cast
hanya merupakan pembungkus stringstream
, ia menderita masalah yang sama yaitu stringstream
: dukungan yang buruk untuk basis nomor ganda dan penanganan kesalahan yang buruk.
Untungnya, seseorang telah menyelesaikan semua masalah di atas. Pustaka standar C berisi strtol
dan keluarga yang tidak memiliki masalah ini.
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;
}
Cukup sederhana untuk sesuatu yang menangani semua kasus kesalahan dan juga mendukung basis nomor dari 2 hingga 36. Jika base
nol (default) itu akan mencoba untuk mengkonversi dari basis apa pun. Atau penelepon dapat memberikan argumen ketiga dan menentukan bahwa konversi hanya boleh dilakukan untuk basis tertentu. Ia tangguh dan menangani semua kesalahan dengan sedikit usaha.
Alasan lain untuk memilih strtol
(dan keluarga):
Sama sekali tidak ada alasan untuk menggunakan metode lain.
strtol
harus aman dari thread. POSIX juga mengharuskan errno
untuk menggunakan penyimpanan thread-lokal. Bahkan pada sistem non-POSIX, hampir semua implementasi errno
pada sistem multithreaded menggunakan penyimpanan thread-local. Standar C ++ terbaru errno
harus sesuai dengan POSIX. Standar C terbaru juga mengharuskan errno
memiliki penyimpanan thread-lokal. Bahkan pada Windows, yang pasti tidak POSIX compliant, errno
adalah benang-aman dan, dengan perluasan, begitu juga strtol
.
std::stol
untuk ini, yang akan dengan tepat melemparkan pengecualian daripada mengembalikan konstanta.
std::stol
bahkan ditambahkan ke bahasa C ++. Yang mengatakan, saya tidak berpikir itu adil untuk mengatakan bahwa ini adalah "C coding dalam C ++". Adalah konyol untuk mengatakan bahwa itu std::strtol
adalah coding C ketika itu secara eksplisit bagian dari bahasa C ++. Jawaban saya diterapkan dengan sempurna ke C ++ ketika ditulis dan itu masih berlaku bahkan dengan yang baru std::stol
. Memanggil fungsi yang dapat menimbulkan pengecualian tidak selalu yang terbaik untuk setiap situasi pemrograman.
Ini adalah cara C yang lebih aman daripada atoi ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ dengan stringstream library standar : (terima kasih CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
Dengan boost library: (terima kasih 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
}
Sunting: Memperbaiki versi stringstream sehingga menangani kesalahan. (Terima kasih atas komentar CMS dan jk pada posting asli)
Cara C lama yang baik masih berfungsi. Saya merekomendasikan strtol atau strtoul. Antara status pengembalian dan 'endPtr', Anda dapat memberikan hasil diagnostik yang baik. Ini juga menangani banyak basis dengan baik.
Anda dapat menggunakan Boost'slexical_cast
, yang membungkus ini dalam antarmuka yang lebih umum.
lexical_cast<Target>(Source)
melempar bad_lexical_cast
pada kegagalan.
Anda dapat menggunakan stringstream dari libraray standar C ++:
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
Status aliran akan diatur ke gagal jika non-digit ditemui ketika mencoba membaca integer.
Lihat Aliran perangkap untuk perangkap penanganan kesalahan dan aliran di C ++.
Anda dapat menggunakan stringstream
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Saya pikir ketiga tautan ini merangkumnya:
solusi stringstream dan lexical_cast hampir sama dengan pemain leksikal menggunakan stringstream.
Beberapa spesialisasi pemeran leksikal menggunakan pendekatan yang berbeda, lihat http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp untuk detailnya. Integer dan float sekarang dikhususkan untuk konversi integer ke string.
Seseorang dapat mengkhususkan lexical_cast untuk kebutuhannya sendiri dan membuatnya cepat. Ini akan menjadi solusi akhir yang memuaskan semua pihak, bersih dan sederhana.
Artikel yang telah disebutkan menunjukkan perbandingan antara berbagai metode konversi bilangan bulat <-> string. Pendekatan berikut masuk akal: c-way lama, spirit.karma, fastformat, loop naif sederhana.
Lexical_cast ok dalam beberapa kasus misalnya untuk konversi int ke string.
Mengubah string menjadi int menggunakan lexical cast bukanlah ide yang baik karena 10-40 kali lebih lambat daripada atoi tergantung pada platform / kompiler yang digunakan.
Boost.Spirit.Karma tampaknya menjadi perpustakaan tercepat untuk mengkonversi integer ke string.
ex.: generate(ptr_char, int_, integer_number);
dan loop sederhana dasar dari artikel yang disebutkan di atas adalah cara tercepat untuk mengkonversi string ke int, jelas bukan yang paling aman, strtol () sepertinya solusi yang lebih aman
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;
}
The C ++ String Toolkit Perpustakaan (StrTk) memiliki solusi berikut:
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 dapat berupa char * unsigned char *, char * atau std :: string iterators, dan T diharapkan menjadi int yang ditandatangani, seperti int yang ditandatangani, int, atau panjang
v = (10 * v) + digit;
meluap sia-sia dengan input string dengan nilai teks INT_MIN
. Tabel bernilai dipertanyakan vs cukupdigit >= '0' && digit <= '9'
Jika Anda memiliki C ++ 11, solusi yang tepat saat ini adalah C ++ bilangan bulat fungsi konversi di <string>
: stoi
,stol
, stoul
, stoll
, stoull
. Mereka memberikan pengecualian yang sesuai ketika diberi input yang salah dan menggunakan fungsi cepat dan kecil di strto*
bawah tenda.
Jika Anda terjebak dengan revisi C ++ yang lebih awal, akan lebih mudah bagi Anda untuk meniru fungsi-fungsi ini dalam implementasi Anda.
Dari C ++ 17 dan seterusnya, Anda dapat menggunakan std::from_chars
dari <charconv>
header seperti yang didokumentasikan di sini .
Sebagai contoh:
#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;
}
}
Sebagai bonus, itu juga bisa menangani pangkalan lain, seperti heksadesimal.
Saya suka jawaban Dan Moulding , saya hanya akan menambahkan sedikit gaya C ++ ke dalamnya:
#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;
}
Ia berfungsi untuk std :: string dan const char * melalui konversi implisit. Ini juga berguna untuk konversi basis, misalnya semua to_int("0x7b")
dan to_int("0173")
dan to_int("01111011", 2)
dan to_int("0000007B", 16)
dan to_int("11120", 3)
dan to_int("3L", 34);
akan mengembalikan 123.
Berbeda dengan std::stoi
itu bekerja di pra-C ++ 11. Juga tidak seperti std::stoi
, boost::lexical_cast
danstringstream
itu melempar pengecualian untuk string aneh seperti "123hohoho".
NB: Fungsi ini mentolerir ruang terdepan tetapi tidak ruang tertinggal, yaitu to_int(" 123")
mengembalikan 123 sambil to_int("123 ")
melempar pengecualian. Pastikan ini dapat diterima untuk kasus penggunaan Anda atau sesuaikan kode.
Fungsi tersebut dapat menjadi bagian dari STL ...
Saya tahu tiga cara mengubah String menjadi int:
Baik menggunakan fungsi stoi (String to int) atau langsung menggunakan Stringstream, cara ketiga untuk menuju konversi individual, Kode di bawah:
Metode 1
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';
Metode 2
#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;
}
Metode 3 - tetapi tidak untuk konversi individu
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 ;
}
Saya suka jawaban Dan , terutama karena menghindari pengecualian. Untuk pengembangan sistem tertanam dan pengembangan sistem tingkat rendah lainnya, mungkin tidak tersedia kerangka kerja Pengecualian yang tepat.
Menambahkan cek untuk ruang putih setelah string yang valid ... tiga baris ini
while (isspace(*end)) {
end++;
}
Menambahkan cek untuk kesalahan parsing juga.
if ((errno != 0) || (s == end)) {
return INCONVERTIBLE;
}
Inilah fungsi lengkapnya ..
#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()
tidak ditentukan untuk mengatur errno
kapan tidak ada konversi terjadi. Lebih baik digunakan if (s == end) return INCONVERTIBLE;
untuk mendeteksi tidak ada konversi. Dan kemudian if (*s == '\0' || *end != '\0')
dapat menyederhanakan ke if (*end)
2) || l > LONG_MAX
dan || l < LONG_MIN
tidak melayani tujuan - mereka tidak pernah benar.
Anda dapat menggunakan metode yang ditentukan ini.
#define toInt(x) {atoi(x.c_str())};
Dan jika Anda mengonversi dari String ke Integer, Anda hanya perlu melakukan hal berikut.
int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}
Outputnya akan menjadi 102.
atoi
tidak tampak seperti "cara C ++," mengingat jawaban lain seperti yang diterima std::stoi()
.
Saya tahu ini adalah pertanyaan yang lebih tua, tetapi saya sudah berkali-kali menjumpainya dan, sampai saat ini, masih belum menemukan solusi dengan templated yang bagus dengan karakteristik sebagai berikut:
Jadi, ini milik saya, dengan tali penguji. Karena menggunakan fungsi C strtoull / strtoll di bawah tenda, ia selalu mengonversi dulu ke jenis terbesar yang tersedia. Kemudian, jika Anda tidak menggunakan tipe terbesar, itu akan melakukan pemeriksaan rentang tambahan untuk memverifikasi jenis Anda tidak lebih dari (di bawah) mengalir. Untuk ini, ini sedikit kurang berkinerja daripada jika seseorang dengan benar memilih strtol / strtoul. Namun, ini juga berfungsi untuk celana pendek / karakter dan, setahu saya, tidak ada fungsi perpustakaan standar yang melakukan itu juga.
Nikmati; semoga seseorang menemukannya bermanfaat.
#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
adalah metode pengguna-tanah; itu kelebihan beban sehingga bisa disebut seperti ini:
int a; a = StringToDecimal<int>("100");
atau ini:
int a; StringToDecimal(a, "100");
Saya benci mengulangi tipe int, jadi lebih suka yang terakhir. Ini memastikan bahwa jika tipe 'a' berubah, maka seseorang tidak mendapatkan hasil yang buruk. Saya berharap kompiler dapat mengetahuinya seperti:
int a; a = StringToDecimal("100");
... tetapi, C ++ tidak menyimpulkan tipe pengembalian templat, jadi itu yang terbaik yang bisa saya dapatkan.
Implementasinya cukup sederhana:
CstrtoxllWrapper
membungkus keduanya strtoull
dan strtoll
, memanggil mana saja yang perlu berdasarkan pada ketandatanganan tipe templat dan memberikan beberapa jaminan tambahan (mis. input negatif tidak diizinkan jika tidak ditandatangani dan memastikan seluruh string dikonversi).
CstrtoxllWrapper
digunakan oleh StringToSigned
dan StringToUnsigned
dengan tipe terbesar (panjang panjang / tak bertanda panjang) tersedia untuk kompiler; ini memungkinkan konversi maksimal dilakukan. Kemudian, jika perlu, StringToSigned
/ StringToUnsigned
melakukan pemeriksaan jangkauan akhir pada tipe yang mendasarinya. Akhirnya, metode titik akhir,StringToDecimal
,, memutuskan mana dari metode templat StringTo * yang akan dipanggil berdasarkan penandatanganan jenis yang mendasarinya.
Saya pikir sebagian besar sampah dapat dioptimalkan oleh kompiler; hampir semuanya harus deterministik saat kompilasi. Setiap komentar tentang aspek ini akan menarik bagi saya!
long long
alih - alih intmax_t
?
if (ePtr != str)
. Selanjutnya, gunakan isspace((unsigned char) *ePtr)
untuk menangani dengan benar nilai negatif dari *ePtr
.
Di C, Anda dapat menggunakan int atoi (const char * str)
,
Parsing str C-string yang menafsirkan kontennya sebagai angka integral, yang dikembalikan sebagai nilai tipe int.
atoi
dalam pertanyaan, saya menyadarinya. Pertanyaannya jelas bukan tentang C, tetapi tentang C ++. -1