Bagaimana cara mengurai string ke int di C ++?

261

Apa cara C ++ mem-parsing string (diberikan sebagai char *) ke dalam int? Penanganan kesalahan yang kuat dan jelas merupakan nilai tambah (bukan mengembalikan nol ).

Eugene Yokota
sumber
21
Bagaimana dengan beberapa contoh dari berikut ini: codeproject.com/KB/recipes/Tokenizer.aspx Mereka sangat efisien dan agak elegan
@ Lee Tou Cheh, jika Anda pikir ini cara yang baik untuk menguraikan int, silakan posting sebagai jawaban.
Eugene Yokota
1
Sama untuk C: stackoverflow.com/questions/7021725/…
Ciro Santilli 郝海东 冠状 病 六四 六四 法轮功

Jawaban:

165

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

CC.
sumber
4
Tetapi mereka menerima argumen lebih dari itu, salah satunya menjadi point to size_t itu, jika bukan nol, diatur ke karakter yang belum bertobat pertama
Zharf
Ya, menggunakan parameter kedua std :: stoi Anda dapat mendeteksi input yang tidak valid. Anda masih harus memutar fungsi konversi sendiri ...
CC.
Sama seperti jawaban yang diterima, tetapi dengan fungsi standar ini yang akan jauh lebih bersih, imo
Zharf
Perlu diingat bahwa argumen kedua dalam fungsi-fungsi ini dapat digunakan untuk mengetahui apakah seluruh string telah dikonversi atau tidak. Jika hasilnya size_ttidak sama dengan panjang string, maka berhenti lebih awal. Itu masih akan mengembalikan 11 dalam kasus itu, tetapi posakan menjadi 2 bukannya panjang string 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29
Zoe
204

Apa yang tidak dilakukan

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 truedan iakan mendapatkan nilai 1337. Kami dapat mengatasi masalah ini dengan memastikan tidak ada lagi karakter dalam stringstreamsetelah 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, stringstreambanyak yang harus diinginkan jika Anda ingin ketahanan dan penanganan kesalahan yang jelas.

Ini menuntun saya ke saran kedua: jangan gunakan Boost lexical_castuntuk ini . Pertimbangkan apa yang dikatakan oleh lexical_castdokumentasi:

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 stringstreammemiliki tingkat kontrol yang buruk, namun dikatakan stringstreamharus digunakan alih-alih lexical_castjika Anda memerlukan "tingkat kontrol yang lebih tinggi". Juga, karena lexical_casthanya merupakan pembungkus stringstream, ia menderita masalah yang sama yaitu stringstream: dukungan yang buruk untuk basis nomor ganda dan penanganan kesalahan yang buruk.

Solusi terbaik

Untungnya, seseorang telah menyelesaikan semua masalah di atas. Pustaka standar C berisi strtoldan 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 basenol (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):

  • Ini menunjukkan kinerja runtime yang jauh lebih baik
  • Ini memperkenalkan overhead kompilasi waktu yang lebih sedikit (yang lainnya menarik hampir 20 kali lebih banyak SLOC dari header)
  • Ini menghasilkan ukuran kode terkecil

Sama sekali tidak ada alasan untuk menggunakan metode lain.

Dan Moulding
sumber
22
@JamesDunne: POSIX strtolharus aman dari thread. POSIX juga mengharuskan errnountuk menggunakan penyimpanan thread-lokal. Bahkan pada sistem non-POSIX, hampir semua implementasi errnopada sistem multithreaded menggunakan penyimpanan thread-local. Standar C ++ terbaru errnoharus sesuai dengan POSIX. Standar C terbaru juga mengharuskan errnomemiliki penyimpanan thread-lokal. Bahkan pada Windows, yang pasti tidak POSIX compliant, errnoadalah benang-aman dan, dengan perluasan, begitu juga strtol.
Dan Moulding
7
Saya tidak bisa mengikuti alasan Anda menentang penggunaan boost :: lexical_cast. Seperti yang mereka katakan, std :: stringstream memang menawarkan banyak kontrol - Anda melakukan segalanya mulai dari pengecekan kesalahan hingga menentukan basis Anda. Dokumentasi saat ini menyatakan seperti ini: "Untuk konversi yang lebih terlibat, seperti di mana presisi atau pemformatan membutuhkan kontrol yang lebih ketat daripada yang ditawarkan oleh perilaku default lexical_cast, direkomendasikan pendekatan std :: stringstream konvensional."
fhd
8
Ini bukan kode C yang tepat di dalam C ++. Pustaka standar berisi std::stoluntuk ini, yang akan dengan tepat melemparkan pengecualian daripada mengembalikan konstanta.
fuzzyTew
22
@ fuzzyTew Saya menulis jawaban ini sebelum std::stolbahkan 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::strtoladalah 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.
Dan Moulding
9
@ fuzzyTew: Kehabisan ruang disk adalah kondisi yang luar biasa. File data yang salah format yang diproduksi komputer adalah kondisi yang luar biasa. Tetapi kesalahan ketik dalam input pengguna tidak luar biasa. Baik untuk memiliki pendekatan parsing yang mampu menangani kegagalan parsing normal dan tidak biasa.
Ben Voigt
67

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)

Luka Marinko
sumber
1
perbarui versi stringstream Anda untuk menyertakan pemeriksaan untuk stringstream :: fail () (seperti yang diminta oleh penanya "Penanganan kesalahan yang jelas dan jelas")
jk.
2
Versi stringstream Anda akan menerima hal-hal seperti "10haha" tanpa mengeluh
Johannes Schaub - litb
3
ubah ke (! (ss >> num) .fail () && (ss >> ws) .of ()) dari ((ss >> num) .fail ()) jika Anda menginginkan penanganan yang sama seperti lexical_cast
Johannes
3
C ++ dengan metode stringstream library tidak berfungsi untuk string seperti "12-SomeString" bahkan dengan cek .fail ().
captonssj
C ++ 11 termasuk fungsi cepat standar untuk ini sekarang
fuzzyTew
21

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.

Chris Arguin
sumber
4
Oh, tolong jangan gunakan hal-hal C lama ini saat pemrograman C ++. Ada cara yang lebih baik / lebih mudah / lebih bersih / lebih modern / lebih aman untuk melakukan ini di C ++!
jk.
27
Lucu ketika orang-orang khawatir tentang cara-cara "lebih modern" untuk menyelesaikan masalah.
J Miller
@Jason, keselamatan jenis dan penanganan kesalahan yang lebih kuat dari IMO adalah ide yang lebih modern dibandingkan dengan C.
Eugene Yokota
6
Saya telah melihat jawaban yang lain, dan sejauh ini tidak ada yang jelas lebih baik / lebih mudah / lebih bersih atau lebih aman. Poster itu mengatakan dia punya char *. Itu membatasi jumlah keamanan yang akan Anda dapatkan :)
Chris Arguin
16

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 ++.

jk.
sumber
2
Metode C ++ stringstream tidak berfungsi untuk string seperti "12-SomeString" bahkan dengan cek 'stream state'.
captonssj
10

Anda dapat menggunakan stringstream

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  ss >> num;
  return num;
}
CMS
sumber
4
Tapi ini tidak menangani kesalahan apa pun. Anda harus memeriksa aliran untuk kegagalan.
jk.
1
Benar Anda harus memeriksa aliran jika ((ss >> num) .fail ()) {// ERROR}
CMS
2
Metode C ++ stringstream tidak berfungsi untuk string seperti "12-SomeString" bahkan dengan cek 'stream state'
captonssj
8

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

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

Rup
sumber
1
PERINGATAN Implementasi ini terlihat bagus, tetapi tidak menangani luapan sejauh yang saya tahu.
Vinnie Falco
2
Kode tidak menangani overflow. v = (10 * v) + digit;meluap sia-sia dengan input string dengan nilai teks INT_MIN. Tabel bernilai dipertanyakan vs cukupdigit >= '0' && digit <= '9'
chux - Reinstate Monica
6

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.

fuzzyTew
sumber
6

Dari C ++ 17 dan seterusnya, Anda dapat menggunakan std::from_charsdari <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.

Pharap
sumber
3

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::stoiitu bekerja di pra-C ++ 11. Juga tidak seperti std::stoi, boost::lexical_castdanstringstream 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 ...

pengguna3925906
sumber
2

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 ;

}
Iqra.
sumber
1

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;
}
pellucide
sumber
@chux menambahkan kode untuk menangani masalah yang Anda sebutkan.
pellucide
1) Masih gagal mendeteksi kesalahan dengan input suka " ". strtol()tidak ditentukan untuk mengatur errnokapan 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_MAXdan || l < LONG_MINtidak melayani tujuan - mereka tidak pernah benar.
chux - Reinstate Monica
@ chux Pada mac, errno diatur untuk kesalahan parsing, tetapi di linux errno tidak disetel. Kode yang diubah bergantung pada "end" pointer untuk mendeteksi itu.
pellucide
0

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.

Boris
sumber
4
idk. Menulis makro sekitar atoitidak tampak seperti "cara C ++," mengingat jawaban lain seperti yang diterima std::stoi().
Eugene Yokota
Saya merasa lebih menyenangkan menggunakan metode yang telah ditentukan: P
Boris
0

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:

  • Dapat mengonversi basis apa pun (dan mendeteksi jenis basis)
  • Akan mendeteksi data yang salah (yaitu memastikan seluruh string, spasi spasi kurang banyak jejak / jejak, dikonsumsi oleh konversi)
  • Akan memastikan bahwa, terlepas dari jenis yang dikonversi, kisaran nilai string dapat diterima.

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

StringToDecimaladalah 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:

CstrtoxllWrappermembungkus keduanya strtoulldan 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).

CstrtoxllWrapperdigunakan oleh StringToSigneddan StringToUnsigneddengan tipe terbesar (panjang panjang / tak bertanda panjang) tersedia untuk kompiler; ini memungkinkan konversi maksimal dilakukan. Kemudian, jika perlu, StringToSigned/ StringToUnsignedmelakukan 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!

DreamWarrior
sumber
"gunakan tipe terbesar" -> mengapa long longalih - alih intmax_t?
chux - Reinstate Monica
Percaya diri Anda inginkan if (ePtr != str). Selanjutnya, gunakan isspace((unsigned char) *ePtr)untuk menangani dengan benar nilai negatif dari *ePtr.
chux - Reinstate Monica
-3

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.

Mamba hitam
sumber
2
Seperti yang saya tautkan atoidalam pertanyaan, saya menyadarinya. Pertanyaannya jelas bukan tentang C, tetapi tentang C ++. -1
Eugene Yokota