Apa cara C ++ mem-parsing string (diberikan sebagai char *) ke dalam int? Penanganan kesalahan yang kuat dan jelas merupakan nilai tambah (bukan mengembalikan nol ).
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 ).
Jawaban:
Di C ++ 11 baru ada fungsi untuk itu: stoi, stol, stoll, stoul dan sebagainya.
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
sumber
size_t
tidak sama dengan panjang string, maka berhenti lebih awal. Itu masih akan mengembalikan 11 dalam kasus itu, tetapipos
akan menjadi 2 bukannya panjang string 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29Apa 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:
Ini memiliki masalah besar:
str2int(i, "1337h4x0r")
dengan senang hati akan kembalitrue
dani
akan mendapatkan nilai1337
. Kami dapat mengatasi masalah ini dengan memastikan tidak ada lagi karakter dalamstringstream
setelah konversi: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-bitint
)? Denganstringstream
, 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 olehlexical_cast
dokumentasi:Apa?? Kami telah melihat bahwa
stringstream
memiliki tingkat kontrol yang buruk, namun dikatakanstringstream
harus digunakan alih-alihlexical_cast
jika Anda memerlukan "tingkat kontrol yang lebih tinggi". Juga, karenalexical_cast
hanya merupakan pembungkusstringstream
, ia menderita masalah yang sama yaitustringstream
: 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
strtol
dan keluarga yang tidak memiliki masalah ini.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.
sumber
strtol
harus aman dari thread. POSIX juga mengharuskanerrno
untuk menggunakan penyimpanan thread-lokal. Bahkan pada sistem non-POSIX, hampir semua implementasierrno
pada sistem multithreaded menggunakan penyimpanan thread-local. Standar C ++ terbaruerrno
harus sesuai dengan POSIX. Standar C terbaru juga mengharuskanerrno
memiliki penyimpanan thread-lokal. Bahkan pada Windows, yang pasti tidak POSIX compliant,errno
adalah benang-aman dan, dengan perluasan, begitu jugastrtol
.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 itustd::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 barustd::stol
. Memanggil fungsi yang dapat menimbulkan pengecualian tidak selalu yang terbaik untuk setiap situasi pemrograman.Ini adalah cara C yang lebih aman daripada atoi ()
C ++ dengan stringstream library standar : (terima kasih CMS )
Dengan boost library: (terima kasih jk )
Sunting: Memperbaiki versi stringstream sehingga menangani kesalahan. (Terima kasih atas komentar CMS dan jk pada posting asli)
sumber
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.
sumber
Anda dapat menggunakan Boost's
lexical_cast
, yang membungkus ini dalam antarmuka yang lebih umum.lexical_cast<Target>(Source)
melemparbad_lexical_cast
pada kegagalan.sumber
Anda dapat menggunakan stringstream dari libraray standar C ++:
Lihat Aliran perangkap untuk perangkap penanganan kesalahan dan aliran di C ++.
sumber
Anda dapat menggunakan stringstream
sumber
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.
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
sumber
The C ++ String Toolkit Perpustakaan (StrTk) memiliki solusi berikut:
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
sumber
v = (10 * v) + digit;
meluap sia-sia dengan input string dengan nilai teksINT_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 distrto*
bawah tenda.Jika Anda terjebak dengan revisi C ++ yang lebih awal, akan lebih mudah bagi Anda untuk meniru fungsi-fungsi ini dalam implementasi Anda.
sumber
Dari C ++ 17 dan seterusnya, Anda dapat menggunakan
std::from_chars
dari<charconv>
header seperti yang didokumentasikan di sini .Sebagai contoh:
Sebagai bonus, itu juga bisa menangani pangkalan lain, seperti heksadesimal.
sumber
Saya suka jawaban Dan Moulding , saya hanya akan menambahkan sedikit gaya C ++ ke dalamnya:
Ia berfungsi untuk std :: string dan const char * melalui konversi implisit. Ini juga berguna untuk konversi basis, misalnya semua
to_int("0x7b")
danto_int("0173")
danto_int("01111011", 2)
danto_int("0000007B", 16)
danto_int("11120", 3)
danto_int("3L", 34);
akan mengembalikan 123.Berbeda dengan
std::stoi
itu bekerja di pra-C ++ 11. Juga tidak sepertistd::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 sambilto_int("123 ")
melempar pengecualian. Pastikan ini dapat diterima untuk kasus penggunaan Anda atau sesuaikan kode.Fungsi tersebut dapat menjadi bagian dari STL ...
sumber
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
Metode 2
Metode 3 - tetapi tidak untuk konversi individu
sumber
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
Menambahkan cek untuk kesalahan parsing juga.
Inilah fungsi lengkapnya ..
sumber
" "
.strtol()
tidak ditentukan untuk mengaturerrno
kapan tidak ada konversi terjadi. Lebih baik digunakanif (s == end) return INCONVERTIBLE;
untuk mendeteksi tidak ada konversi. Dan kemudianif (*s == '\0' || *end != '\0')
dapat menyederhanakan keif (*end)
2)|| l > LONG_MAX
dan|| l < LONG_MIN
tidak melayani tujuan - mereka tidak pernah benar.Anda dapat menggunakan metode yang ditentukan ini.
Dan jika Anda mengonversi dari String ke Integer, Anda hanya perlu melakukan hal berikut.
Outputnya akan menjadi 102.
sumber
atoi
tidak tampak seperti "cara C ++," mengingat jawaban lain seperti yang diterimastd::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.
StringToDecimal
adalah metode pengguna-tanah; itu kelebihan beban sehingga bisa disebut seperti ini:atau ini:
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:
... tetapi, C ++ tidak menyimpulkan tipe pengembalian templat, jadi itu yang terbaik yang bisa saya dapatkan.
Implementasinya cukup sederhana:
CstrtoxllWrapper
membungkus keduanyastrtoull
danstrtoll
, 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 olehStringToSigned
danStringToUnsigned
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!
sumber
long long
alih - alihintmax_t
?if (ePtr != str)
. Selanjutnya, gunakanisspace((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.
sumber
atoi
dalam pertanyaan, saya menyadarinya. Pertanyaannya jelas bukan tentang C, tetapi tentang C ++. -1