Bir dize C ++ bir int ayrıştırma nasıl?


261

Bir karakter dizisini (char * olarak verilir) int olarak ayrıştırmanın C ++ yolu nedir? Sağlam ve net hata işleme bir artıdır ( sıfır döndürmek yerine ).


21
Aşağıdakilerden bazı örneklere ne dersiniz: codeproject.com/KB/recipes/Tokenizer.aspx Çok verimli ve biraz zarifler

@ To To Che Che, int'i ayrıştırmanın iyi bir yolu olduğunu düşünüyorsanız, lütfen cevap olarak gönderin.
Eugene Yokota

Yanıtlar:


165

Yeni C ++ 11'de bunun işlevleri vardır: stoi, stol, stoll, stoul vb.

int myNr = std::stoi(myString);

Dönüşüm hatası için bir istisna atar.

Bu yeni işlevler bile aynı sorunu yaşıyor Dan'ın belirttiği : "11x" dizesini "11" tamsayısına dönüştürürler.

Daha fazlasını görün: http://en.cppreference.com/w/cpp/string/basic_string/stol


4
Ancak bundan daha fazla argüman kabul ediyorlar, bunlardan biri size_t için bir nokta, null değilse, ilk dönüştürülmemiş karaktere ayarlanmış
Zharf

Evet, std :: stoi'nin ikinci parametresini kullanarak geçersiz giriş algılayabilirsiniz. Yine de kendi dönüşüm fonksiyonunuzu yuvarlamanız gerekiyor ...
CC.

Kabul edilen cevabın yaptığı gibi, ama çok daha temiz olan bu standart fonksiyonlarla, imo
Zharf

Bu işlevlerdeki ikinci bağımsız değişkenin, dizenin tamamının dönüştürülüp dönüştürülmediğini söylemek için kullanılabileceğini unutmayın. Sonuç size_t, dizenin uzunluğuna eşit değilse, erken durdu. Bu durumda yine de 11 döndürür, ancak posdize uzunluğu 3 yerine 2 olur. Coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Zoe

204

Ne yapmamalı

İşte benim ilk tavsiyem: Bunun için stringstream kullanmayın . İlk başta kullanımı basit gibi görünse de, sağlamlık ve iyi hata işleme 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;
}

Bu büyük bir sorun var: str2int(i, "1337h4x0r")mutlulukla dönecek trueve ideğeri alacak 1337. stringstreamDönüşümden sonra başka karakter kalmadığından emin olarak bu sorunu çözebiliriz :

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 sorunu düzelttik, ancak hala birkaç sorun daha var.

Dizedeki sayı temel 10 değilse ne olur? ss << std::hexDönüştürmeyi denemeden önce akışı doğru moda (ör. ) Ayarlayarak diğer üsleri barındırmayı deneyebiliriz . Ancak bu, arayan kişinin bir önsezi bilmesi gerektiği anlamına gelir numarayı gereken sayıdır - arayan kişi bunu nasıl bilebilir? Arayan kişi numaranın ne olduğunu henüz bilmiyor. Bunun olduğunu bile bilmiyorlarbir sayı! Bunun nasıl bir temel olduğunu bilmesi nasıl beklenebilir? Programlarımıza girilen tüm sayıların temel 10 olması ve onaltılık veya sekizlik girdiyi geçersiz olarak reddetmesi gerektiğini söyleyebiliriz. Ancak bu çok esnek veya sağlam değildir. Bu sorunun basit bir çözümü yoktur. Her bir taban için dönüşümü bir kez deneyemezsiniz, çünkü ondalık dönüşüm her zaman sekizli sayılar için (baştaki sıfır ile) başarılı olur ve sekizlik dönüşüm bazı ondalık sayılar için başarılı olabilir. Şimdi önde sıfır olup olmadığını kontrol etmelisiniz. Fakat bekle! Onaltılık sayılar da baştaki sıfır ile başlayabilir (0x ...). İç çekmek.

Yukarıdaki sorunlarla başa çıksanız bile, daha büyük bir sorun daha var: arayanın hatalı giriş (örneğin "123foo") ile aralığının dışında bir sayı int( 32 bit int)? İle stringstream, bu ayrımı yapmanın bir yolu yoktur. Yalnızca dönüşümün başarılı veya başarısız olduğunu biliyoruz. Başarısız olursa, neden başarısız olduğunu bilmenin hiçbir yolu yoktur . Gördüğünüz gibi, stringstreamsağlamlık ve net hata yönetimi istiyorsanız, arzulanan çok şey bırakıyor.

Tavsiye benim ikinci parçasına Bu potansiyel müşteriler beni: faydası yok Boost var yapmak lexical_castbunun için . lexical_castBelgelerin neler söyleyeceğini düşünün :

Dönüşümler üzerinde daha yüksek düzeyde kontrol gerektiğinde, std :: stringstream ve std :: wstringstream daha uygun bir yol sunar. Akışa dayalı olmayan dönüşümlerin gerekli olduğu durumlarda, lexical_cast iş için yanlış araçtır ve bu tür senaryolar için özel bir kasa değildir.

Ne?? Bunun stringstreamdaha düşük bir kontrol seviyesine sahip olduğunu gördük ve yine de "daha yüksek bir kontrol seviyesine" ihtiyacınız olduğunda stringstreamkullanılması gerektiğini söylüyor lexical_cast. Ayrıca, lexical_castsadece bir sarıcı stringstreamolduğu için, aynı problemlerden muzdariptir stringstream: çoklu sayı tabanları için zayıf destek ve zayıf hata işleme.

En iyi çözüm

Neyse ki, bir kişi yukarıdaki sorunların hepsini zaten çözdü. C standart kütüphanesi strtolve bu problemlerin hiçbirine sahip olmayan bir aile içerir .

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ı işleyen ve ayrıca 2'den 36'ya kadar herhangi bir sayı tabanını destekleyen bir şey için oldukça basittir. baseSıfır ise (varsayılan) herhangi bir tabandan dönüştürmeye çalışacaktır. Veya arayan üçüncü argümanı sağlayabilir ve dönüşümün yalnızca belirli bir temel için denenmesi gerektiğini belirtebilir. Sağlamdır ve tüm hataları en az çaba harcayarak işler.

Tercih etmek için diğer nedenler strtol(ve aile):

  • Çok daha iyi çalışma zamanı performansı gösterir
  • Daha az derleme zamanı ek yükü getirir (diğerleri başlıklardan yaklaşık 20 kat daha fazla SLOC çeker)
  • En küçük kod boyutuyla sonuçlanır

Başka bir yöntem kullanmak için kesinlikle iyi bir neden yoktur.


22
@JamesDunne: POSIX'in strtoliş parçacığı için güvenli olması gerekir . POSIX ayrıca errnoyerel iş parçacığı depolama birimini de gerektirir . POSIX olmayan sistemlerde bile, errnoçok iş parçacıklı sistemlerin neredeyse tüm uygulamaları yerel iş parçacığı depolamasını kullanır. En son C ++ standardının errnoPOSIX uyumlu olması gerekir. En yeni C standardı da errnoyerel olarak yerel depolamaya sahip olmayı gerektirir . Kesinlikle POSIX uyumlu olmayan Windows'ta bile errno, iş parçacığı için güvenlidir ve uzantı ile de öyle strtol.
Dan Moulding

7
Boost :: lexical_cast kullanarak akıl yürütmenizi gerçekten takip edemiyorum. Dedikleri gibi, std :: stringstream gerçekten çok fazla kontrol sunuyor - hata kontrolünden kendinizi belirlemenize kadar her şeyi yapıyorsunuz. Mevcut belgeler şu şekilde ortaya koymaktadır: "Kesinlik veya biçimlendirmenin lexical_cast'in varsayılan davranışı tarafından sunulandan daha sıkı kontrole ihtiyaç duyduğu gibi daha ilgili dönüşümler için geleneksel std :: stringstream yaklaşımı önerilir."
fhd

8
Bu, C ++ içindeki uygunsuz C kodlamasıdır. Standart kütüphane std::stolbunun için, sabitleri döndürmek yerine uygun şekilde istisnalar atacaktır.
fuzzyTew

22
@fuzzyTew Bu cevabı daha önce yazmıştım std::stolbile C ++ diline eklendi. Bununla birlikte, bunun "C ++ içinde C kodlaması" olduğunu söylemenin adil olduğunu düşünmüyorum. std::strtolAçıkça C ++ dilinin bir parçası olduğunda C kodlaması olduğunu söylemek saçmadır . Cevabım yazıldığı zaman C ++ 'a mükemmel bir şekilde uygulandı ve hala yeni ile bile geçerli std::stol. İstisnalar atabilecek işlevleri çağırmak her programlama durumu için her zaman en iyisi değildir.
Dan Moulding

9
@fuzzyTew: Disk alanınızın tükenmesi istisnai bir durumdur. Bilgisayarda üretilen hatalı biçimlendirilmiş veri dosyaları istisnai bir durumdur. Ancak kullanıcı girişindeki yazım hataları istisnai değildir. Normal, olağandışı ayrıştırma hatalarını işleyebilen bir ayrıştırma yaklaşımına sahip olmak iyidir.
Ben Voigt

67

Bu, atoi () 'den daha güvenli bir C yoludur

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

Standart kitaplık stringstream ile C ++ : (teşekkürler CMS )

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

İle boost kütüphanesinden: (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: Dize akışı sürümünü hataları işleyecek şekilde düzeltildi. (CMS'ler ve jk'ın orijinal gönderi hakkındaki yorumları sayesinde)


1
lütfen stringstream sürümünüzü stringstream :: fail () ("Sağlam ve net hata işleme" sorgusu tarafından istendiği gibi) içerecek şekilde güncelleyin
jk.

2
Dize akışı sürümünüz şikayet etmeden "10haha" gibi şeyleri kabul edecektir
Johannes Schaub - litb

3
lexical_cast
Johannes

3
Standart kitaplık stringstream yöntemi ile C ++ .fail () denetimi ile bile "12-SomeString" gibi dizeler için çalışmaz.
captonssj

C ++ 11 bunun için standart hızlı işlevler içeriyor
fuzzyTew

21

İyi 'eski C yolu hala çalışıyor. Ben strtol veya strtoul öneririm. Dönüş durumu ile 'endPtr' arasında, iyi tanı çıktısı verebilirsiniz. Aynı zamanda birden fazla üssü güzelce idare eder.


4
Oh lütfen C ++ programlarken bu eski C şeylerini kullanmayın. C ++ bunu yapmak için daha iyi / daha kolay / temiz / daha modern / daha güvenli yolları vardır!
jk.

27
İnsanlar bir sorunu çözmek için "daha modern" yollar hakkında endişe duyduklarında komiktir.
J Miller

@Jason, IMO daha güçlü tip güvenlik ve hata yönetimi, C ile karşılaştırıldığında daha modern bir fikirdir.
Eugene Yokota

6
Diğer cevaplara baktım ve şimdiye kadar hiçbir şey daha iyi / daha kolay / daha temiz veya daha güvenli değil. Afiş, karakterinin olduğunu söyledi *. Bu, alacağınız güvenlik miktarını sınırlar :)
Chris Arguin


16

C ++ standart kütüphanesinden bir dize akışını kullanabilirsiniz:

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

Bir tam sayı okunmaya çalışılırken rakam olmayan bir durumla karşılaşılırsa akış durumu başarısız olacak şekilde ayarlanır.

C ++ 'da hata işleme ve akış tuzakları için Akış tuzaklarına bakın .


2
C ++ dize akışı yöntemi, 'akış durumu' denetiminde bile "12-SomeString" gibi dizeler için çalışmaz.
captonssj

10

Stringstream'leri kullanabilirsiniz

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}

4
Ancak bu herhangi bir hatayı işlemez. Akışta hata olup olmadığını kontrol etmelisiniz.
jk.

1
Doğru ((ss >> num) .fail ()) {// HATA}
CMS

2
C ++ dize akışı yöntemi, 'akış durumu' denetimi ile bile "12-SomeString" gibi dizeler için çalışmaz
captonssj 12:30

8

Bence bu üç bağlantı özetliyor:

stringstream ve lexical_cast çözümleri, lexical cast'ın stringstream kullandığı ile hemen hemen aynıdır.

Sözcük dağarcığının bazı uzmanlıkları farklı yaklaşımlar kullanır , ayrıntılar için http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp adresine bakın. Tamsayılar ve kayan noktalar artık tamsayıdan dizeye dönüştürme için uzmanlaşmıştır.

Lexical_cast kendi ihtiyaçları için uzmanlaşabilir ve hızlı bir şekilde yapılabilir. Bu, temiz ve basit tüm tarafları tatmin eden nihai çözüm olacaktır.

Daha önce zikredilen makaleler, tamsayılar <-> dizelerini dönüştürmenin farklı yöntemleri arasındaki karşılaştırmayı göstermektedir. Aşağıdaki yaklaşımlar mantıklıdır: eski c-yolu, ruh. Karma, fastformat, basit saf döngü.

Lexical_cast bazı durumlarda, örneğin int'ten string'e dönüştürme için uygundur.

Diziyi lexical cast kullanarak int'e dönüştürmek, kullanılan platforma / derleyiciye bağlı olarak atoi'den 10-40 kat daha yavaş olduğu için iyi bir fikir değildir.

Boost.Spirit.Karma, tamsayıyı dizeye dönüştürmek için en hızlı kütüphane gibi görünüyor.

ex.: generate(ptr_char, int_, integer_number);

ve yukarıda bahsedilen makaledeki basit basit döngü, dizeyi int'e dönüştürmenin en hızlı yoludur, açıkçası en güvenli olana değil, strtol () daha güvenli bir çözüm gibi görünüyor

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;
}

7

C ++ Yaylı Toolkit Kütüphanesi (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 işaretsiz char *, char * veya std :: string yineleyicileri olabilir ve T'nin işaretli int, int veya uzun gibi işaretli bir int olması beklenir.


1
UYARI Bu uygulama hoş görünüyor, ancak taşabileceğim kadarıyla işlenemiyor.
Vinnie Falco

2
Kod taşma işlemiyor. v = (10 * v) + digit;metin değeri ile dize girişi ile gereksiz yere taşar INT_MIN. Tablo şüpheli değer vs basitçedigit >= '0' && digit <= '9'
chux - Reinstate Monica

6

Eğer C ++ 11 varsa, uygun çözümler günümüzde dönüşüm işlevleri tamsayı C ++ şunlardır <string>: stoi, stol, stoul, stoll, stoull. Yanlış giriş yapıldığında uygun istisnalar atarlar strto*ve kaputun altındaki hızlı ve küçük fonksiyonları kullanırlar .

C ++ 'nın daha önceki bir revizyonuna takılı kalırsanız, uygulamanızda bu işlevleri taklit etmeniz ileriye taşınabilir.


6

C ++ 17'den itibaren std::from_chars, burada<charconv> belgelendiği gibi başlıktan kullanabilirsiniz .

Ö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 ele alabilir.


3

Dan Moulding'in cevabını seviyorum , sadece biraz C ++ stili ekleyeceğim:

#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;
}

Örtük dönüşüm yoluyla hem std :: string hem de const char * için çalışır. Ayrıca temel dönüşüm için de faydalıdır, örneğin tüm to_int("0x7b")ve to_int("0173")ve to_int("01111011", 2)ve to_int("0000007B", 16)ve to_int("11120", 3)veto_int("3L", 34); 123 dönecekti.

Aksine std::stoiC ++ 11 öncesi çalışır. Ayrıca farklı std::stoi, boost::lexical_castvestringstream bu "123hohoho" gibi garip dizeleri için istisnalar atar.

Not: Bu işlev öndeki boşlukları tolere eder, ancak arkadaki boşlukları tolere etmez, yani istisna to_int(" 123")atarken 123 döndürür to_int("123 "). Bunun kullanım durumunuz için kabul edilebilir olduğundan emin olun veya kodu ayarlayın.

Böyle bir fonksiyon STL'nin bir parçası olabilir ...


2

Int içine String dönüştürmenin üç yolu biliyorum:

Stoi (String to int) işlevini kullanın ya da tek tek dönüştürmenin üçüncü yolu olan Stringstream ile gidin, Code aşağıdadır:

1. Yöntem

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. Yöntem

#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;
} 

3. Yöntem - ancak tek bir dönüşüm için değil

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 ;

}

1

Dan'ın cevabını seviyorum , özellikle de istisnalardan kaçınmak. Gömülü sistem geliştirme ve diğer düşük düzeyli sistem geliştirme için uygun bir İstisna çerçevesi olmayabilir.

Geçerli bir dizeden sonra boşluk için bir kontrol eklendi ... bu üç satır

    while (isspace(*end)) {
        end++;
    }


Ayrıştırma hataları için de bir kontrol eklendi.

    if ((errno != 0) || (s == end)) {
        return INCONVERTIBLE;
    }


İşte tam fonksiyon ..

#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;
}

@chux, bahsettiğiniz endişeleri gidermek için kod ekledi.
pellucide

1) gibi giriş ile hala hata tespit edemiyoruz " ". hiçbir dönüşüm gerçekleşmediğinde strtol()ayarlanacak şekilde belirtilmez errno. if (s == end) return INCONVERTIBLE; Dönüşüm tespit etmek için kullanmak daha iyidir. Ve sonra 2'ye if (*s == '\0' || *end != '\0')sadeleştirebilir if (*end)) || l > LONG_MAXve || l < LONG_MINhiçbir amaca hizmet etmezler - asla doğru değildir.
chux - Monica'yı geri yükle

@chux Mac'te, errno hataları ayrıştırma için ayarlanmıştır, ancak linux'da errno ayarlanmamıştır. Kodu saptamak için "son" işaretçisine bağlı olarak değiştirildi.
pellucide

0

Bu tanımlı yöntemi kullanabilirsiniz.

#define toInt(x) {atoi(x.c_str())};

Ve eğer String'den bir Tamsayıya dönüştüyseniz, aşağıdakileri yapardınız.

int main()
{
string test = "46", test2 = "56";
int a = toInt(test);
int b = toInt(test2);
cout<<a+b<<endl;
}

Çıktı 102 olur.


4
Bilmiyorum. Etrafında tanımlanmış bir makro yazmak atoi, kabul edilen diğer cevaplar ışığında "C ++ yolu" gibi görünmüyor std::stoi().
Eugene Yokota

Önceden tanımlanmış yöntemleri kullanarak daha eğlenceli buluyorum: P
Boris

0

Bunun daha eski bir soru olduğunu biliyorum, ancak birçok kez karşılaştım ve bugüne kadar aşağıdaki özelliklere sahip güzel bir şablon bulamadık:

  • Herhangi bir tabanı dönüştürebilir (ve baz türünü algılayabilir)
  • Hatalı verileri algılar (yani, dizenin tamamının, daha az öndeki / izleyen boşluk olan, dönüşüm tarafından tüketildiğinden emin olun)
  • Dönüştürülen türe bakılmaksızın, dizenin değer aralığının kabul edilebilir olmasını sağlar.

İşte, test kayışı ile benim. Kaputun altında strtoull / strtoll C işlevlerini kullandığı için, her zaman önce mevcut en büyük türe dönüştürür. Daha sonra, en büyük türü kullanmıyorsanız, türünüzün fazla (altında) akmadığını doğrulamak için ek aralık kontrolleri gerçekleştirir. Bunun için, strtol / strtoul'u düzgün seçmiş olmaktan biraz daha az performans gösterir. Bununla birlikte, şort / chars için de çalışır ve bildiğim kadarıyla bunu yapan standart bir kütüphane işlevi yoktur.

Zevk almak; umarım birisi faydalı bulur.

#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;
}

StringToDecimalkullanıcı-arazi yöntemidir; aşırı yüklenir, böylece şu şekilde çağrılabilir:

int a; a = StringToDecimal<int>("100");

veya bu:

int a; StringToDecimal(a, "100");

İnt türünü tekrarlamaktan nefret ediyorum, bu yüzden ikincisini tercih edin. Bu, 'a' tipi değişirse kötü sonuç alınmamasını sağlar. Derleyici şöyle çözebilir:

int a; a = StringToDecimal("100");

... ancak, C ++ şablon dönüş türlerini çıkarmaz, bu yüzden alabileceğim en iyisi budur.

Uygulama oldukça basit:

CstrtoxllWrapperher ikisini de sarar strtoullve strtollşablon türünün imzalı olmasına bağlı olarak gerekli olanları çağırmak ve bazı ek garantiler sağlamak (örneğin, imzalanmamışsa negatif girdiye izin verilmez ve tüm dizgenin dönüştürülmesini sağlar).

CstrtoxllWrapperderleyici tarafından kullanılabilen en büyük tip (uzun uzun / imzasız uzun uzun) tarafından StringToSignedve StringToUnsignedile kullanılır; bu, maksimum dönüşümün gerçekleştirilmesini sağlar. Daha sonra, gerekliyse, StringToSigned/ StringToUnsignedaltta yatan tipte son aralık kontrollerini yapar. Son olarak, bitiş noktası yöntemi, StringToDecimaltemel türün imzalı olmasına bağlı olarak StringTo * şablon yöntemlerinden hangisinin çağrılacağına karar verir.

Önemsizlerin çoğu derleyici tarafından optimize edilebilir düşünüyorum; hemen hemen her şey derleme zamanı belirleyicisi olmalıdır. Bu konudaki herhangi bir yorum benim için ilginç olurdu!


"en büyük türü kullan" -> neden long longyerine intmax_t?
chux - Monica'yı eski durumuna döndür

İstediğinizden emin if (ePtr != str). Ayrıca, isspace((unsigned char) *ePtr)negatif değerlerini düzgün işlemek için kullanın *ePtr.
chux - Monica'yı eski durumuna döndür

-3

C dilinde şunları kullanabilirsiniz int atoi (const char * str):

İnt türünde bir değer olarak döndürülen içeriğini integral bir sayı olarak yorumlayan C-string str'yi ayrıştırır.


2
Soruda bağlantı atoikurduğumdan bunun farkındayım. Soru açıkça C ile ilgili değil, C ++ ile ilgili. -1
Eugene Yokota
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.