Bir dize (char * olarak verilen) bir diziyi ayrıştırmanın C ++ yolu nedir? Sağlam ve net hata işleme bir artıdır (yerine sıfır döndürme).
Bir dize (char * olarak verilen) bir diziyi ayrıştırmanın C ++ yolu nedir? Sağlam ve net hata işleme bir artıdır (yerine sıfır döndürme).
Yeni C ++ 11'de bunun için fonksiyonlar var: stoi, stol, stoll, stoul vb.
int myNr = std::stoi(myString);
Dönüşüm hatasına bir istisna atar.
Bu yeni işlevler bile hala aynı sorun Dan tarafından belirtildiği gibi: "11x" dizesini "11" tamsayısına dönüştürürler.
Daha fazla gör: http://en.cppreference.com/w/cpp/string/basic_string/stol
İşte ilk tavsiyem: Bunun için stringstream kullanmayın. İlk başta kullanımı kolay görünse de, sağlamlık ve iyi hata işlemeyi istiyorsanız çok fazla iş yapmanız gerektiğini göreceksiniz.
İşte, sezgisel olarak çalışması gerektiği gibi görünen bir yaklaşım:
bool str2int (int &i, char const *s)
{
std::stringstream ss(s);
ss >> i;
if (ss.fail()) {
// not an integer
return false;
}
return true;
}
Bunun büyük bir sorunu var: str2int(i, "1337h4x0r")
mutlulukla dönecek true
ve i
değeri alacak 1337
. Daha fazla karakter kalmamasını sağlayarak bu soruna geçici bir çözüm bulabiliriz. stringstream
dönüşümden sonra:
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;
}
Bir problemi çözdük, ama hala birkaç problem daha var.
Eğer dizideki sayı baz 10 değilse? Akışı doğru moda ayarlayarak diğer tabanları yerleştirmeye çalışabiliriz (ör. ss << std::hex
) dönüşümü denemeden önce. Ancak bu, arayanın bilmesi gerektiği anlamına gelir Önsel Sayı ne kadardır - ve arayan kişi bunu nereden biliyor? Arayan numaranın ne olduğunu bilmiyor. Onu bile bilmiyorlar. olduğu bir sayı! Hangi tabanın olduğunu bilmek nasıl beklenebilir? Programlarımıza giren tüm sayıların 10 bazında olması ve onaltılık veya sekizli girişi geçersiz olarak reddetmesi emrini verebiliriz. Ancak bu çok esnek veya sağlam değildir. Bu problem için basit bir çözüm yoktur. Her bir taban için dönüşümü bir kez denemezsiniz, çünkü ondalık dönüşüm her zaman sekizlik sayılar (baştaki sıfır ile) için başarılı olur ve sekizlik dönüşüm bazı ondalık sayılar için başarılı olabilir. Öyleyse şimdi baştaki bir sırayı kontrol etmelisin. Fakat bekle! Onaltılık sayılar da bir sıfır ile başlayabilir (0x ...). İç çekmek.
Yukarıdaki problemlerle başa çıkmada başarılı olsanız bile, daha büyük bir problem daha vardır: eğer arayan kişi, kötü girdi (ör. "123foo") ve aralığın dışında kalan bir sayı arasında ayrım yapmak isterse ne olur? int
(ör. 32-bit için "4000000000" int
)? İle stringstream
Bu ayrımı yapmanın bir yolu yoktur. Yalnızca dönüşümün başarılı olup olmadığını biliyoruz. Başarısız olursa, bilmenin bir yolu yoktur. niye ya başarısız oldu. Gördüğün gibi, stringstream
Sağlamlık ve net hata işlemeyi istiyorsanız çok arzu edilen bırakır.
Bu beni ikinci tavsiyemle yönlendiriyor: Boost yok lexical_cast
bunun için. Ne olduğunu düşünün lexical_cast
dokümantasyon şunları söylemelidir:
Daha yüksek derecede kontrolün olduğu yer dönüşümler için gerekli std :: stringstream ve std :: wstringstream daha fazlasını sunuyor uygun yol Nerede akışa dayalı olmayan dönüşümler gerekli, lexical_cast yanlış iş için araç ve değil Bu tür senaryolar için özel kasa.
Ne?? Bunu daha önce görmüştük stringstream
zayıf bir kontrol seviyesine sahip, ve yine de diyor stringstream
yerine kullanılmalıdır lexical_cast
"Daha yüksek bir kontrol seviyesi" ye ihtiyacınız varsa. Ayrıca, çünkü lexical_cast
sadece bir sarıcı stringstream
, aynı sorunlardan muzdarip stringstream
yapar: çoklu sayı üsleri için zayıf destek ve hatalı hata işleme.
Neyse ki, birisi yukarıdaki problemlerin hepsini zaten çözmüştür. C standart kütüphanesi içerir strtol
ve bu sorunların hiçbirine sahip olmayan aile.
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;
}
Tüm hata durumlarını ele alan ve ayrıca 2'den 36'ya kadar herhangi bir sayı tabanını destekleyen bir şey için oldukça basittir. base
sıfır (varsayılan) herhangi bir tabandan dönüştürmeyi deneyecektir. Veya arayan üçüncü argümanı sağlayabilir ve dönüşümün sadece belirli bir baz için denenmesi gerektiğini belirleyebilir. Sağlamdır ve tüm hataları minimum çaba ile ele alır.
Tercih etmek için diğer nedenler strtol
(ve aile):
Başka bir yöntem kullanmanın kesinlikle iyi bir nedeni yoktur.
Bu atoi'den daha güvenli bir C ()
const char* str = "123";
int i;
if(sscanf(str, "%d", &i) == EOF )
{
/* error */
}
C ++ standart kütüphane ile stringstream: (Teşekkürler CMS )
int str2int (const string &str) {
stringstream ss(str);
int num;
if((ss >> num).fail())
{
//ERROR
}
return num;
}
İle artırmak kütüphane: (teşekkürler 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
}
Düzenleme: Stringstream versiyonunu düzeltip hataları ele alır. (CMS'nin ve jk'in orjinal yazıya yaptığı yorum sayesinde)
Kullanabilirsiniz Boost en lexical_cast
, hangi bunu sarar daha genel bir arayüzde.
lexical_cast<Target>(Source)
atar bad_lexical_cast
başarısızlık durumunda.
İyi 'eski C yolu hala çalışıyor. Strtol veya strtoul'u tavsiye ederim. Dönüş durumu ile 'endPtr' arasında, iyi bir teşhis çıkışı sağlayabilirsiniz. Aynı zamanda çok sayıda üsleri de ele alır.
C ++ standart kütüphanesinden bir stringstream kullanabilirsiniz:
stringstream ss(str);
int x;
ss >> x;
if(ss) { // <-- error handling
// use x
} else {
// not a number
}
Akış durumu başarısız olacak şekilde ayarlanacak ne zaman bir non-digit ile karşılaşılırsa bir tamsayı okumaya çalışıyorum.
Görmek Akış tuzakları C ++ 'da hata bulma ve akışların tuzakları için.
Kullanabilirsiniz stringstream en
int str2int (const string &str) {
stringstream ss(str);
int num;
ss >> num;
return num;
}
Bu üç bağlantıyı özetledim:
C ++ Dize Toolkit Kitaplığı (StrTk) aşağıdaki çözüme sahiptir:
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, imzasız char *, char * veya std :: string yineleyicileri olabilir ve T'nin imzalı int, int veya long gibi imzalı bir int olması beklenir.
C ++ 11'iniz varsa, uygun çözümler şu anda C ++ tamsayı dönüşüm işlevleridir. <string>
: stoi
, stol
, stoul
, stoll
, stoull
. Yanlış giriş verildiğinde uygun istisnalar atar ve hızlı ve küçük kullanır strto*
başlık altındaki fonksiyonlar.
C ++ 'nın daha önceki bir revizyonuna takılırsanız, bu fonksiyonları uygulamanızda taklit etmeniz sizin için ileriye taşınabilir olacaktır.
C ++ 17'den itibaren kullanabilirsiniz std::from_chars
itibaren <charconv>
başlıklı belge İşte.
Örneğin:
#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;
}
}
Bir bonus olarak, onaltılık gibi diğer üsleri de işleyebilir.