Kenapa itu scanf
buruk?
Masalah utama adalah yang scanf
tidak pernah dimaksudkan untuk berurusan dengan input pengguna. Ini dimaksudkan untuk digunakan dengan data yang diformat "dengan sempurna". Saya mengutip kata "sempurna" karena itu tidak sepenuhnya benar. Tapi itu tidak dirancang untuk mem-parsing data yang tidak dapat diandalkan seperti input pengguna. Secara alami, input pengguna tidak dapat diprediksi. Pengguna salah mengerti instruksi, membuat kesalahan ketik, secara tidak sengaja tekan enter sebelum dilakukan dll. Orang mungkin bertanya mengapa fungsi yang seharusnya tidak digunakan untuk input pengguna dibaca dari stdin
. Jika Anda adalah pengguna * nix yang berpengalaman, penjelasannya tidak akan mengejutkan tetapi mungkin membingungkan pengguna Windows. Dalam sistem * nix, sangat umum untuk membangun program yang bekerja melalui perpipaan,stdout
stdin
dari yang kedua. Dengan cara ini, Anda dapat memastikan bahwa output dan input dapat diprediksi. Selama keadaan ini, scanf
sebenarnya berfungsi dengan baik. Tetapi ketika bekerja dengan input yang tidak dapat diprediksi, Anda berisiko segala macam masalah.
Jadi mengapa tidak ada fungsi standar yang mudah digunakan untuk input pengguna? Orang hanya bisa menebak di sini, tapi saya berasumsi bahwa peretas tua hardcore C hanya berpikir bahwa fungsi yang ada cukup baik, meskipun mereka sangat kikuk. Juga, ketika Anda melihat aplikasi terminal tipikal mereka sangat jarang membaca input pengguna stdin
. Paling sering Anda melewati semua input pengguna sebagai argumen baris perintah. Tentu, ada pengecualian, tetapi untuk sebagian besar aplikasi, input pengguna adalah hal yang sangat kecil.
Jadi apa yang bisa kamu lakukan?
Favorit saya adalah fgets
kombinasi dengan sscanf
. Saya pernah menulis jawaban tentang itu, tetapi saya akan memposting ulang kode lengkap. Berikut adalah contoh dengan pengecekan dan parsing kesalahan yang layak (tapi tidak sempurna). Cukup bagus untuk keperluan debugging.
Catatan
Saya tidak terlalu suka meminta pengguna untuk memasukkan dua hal yang berbeda pada satu baris. Saya hanya melakukan itu ketika mereka saling memiliki secara alami. Suka misalnya printf("Enter the price in the format <dollars>.<cent>: ")
dan kemudian gunakan sscanf(buffer "%d.%d", &dollar, ¢)
. Saya tidak akan pernah melakukan hal seperti itu printf("Enter height and base of the triangle: ")
. Poin utama menggunakan di fgets
bawah ini adalah untuk merangkum input untuk memastikan bahwa satu input tidak mempengaruhi yang berikutnya.
#define bsize 100
void error_function(const char *buffer, int no_conversions) {
fprintf(stderr, "An error occurred. You entered:\n%s\n", buffer);
fprintf(stderr, "%d successful conversions", no_conversions);
exit(EXIT_FAILURE);
}
char c, buffer[bsize];
int x,y;
float f, g;
int r;
printf("Enter two integers: ");
fflush(stdout); // Make sure that the printf is executed before reading
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);
// Unless the input buffer was to small we can be sure that stdin is empty
// when we come here.
printf("Enter two floats: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);
// Reading single characters can be especially tricky if the input buffer
// is not emptied before. But since we're using fgets, we're safe.
printf("Enter a char: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%c", &c)) != 1) error_function(buffer, r);
printf("You entered %d %d %f %c\n", x, y, f, c);
Jika Anda melakukan banyak ini, saya bisa merekomendasikan membuat pembungkus yang selalu memerah:
int printfflush (const char *format, ...)
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
fflush(stdout);
va_end (arg);
return done;
}```
Melakukan hal seperti ini akan menghilangkan masalah umum, yaitu trailing newline yang dapat mengacaukan input sarang. Tetapi memiliki masalah lain, yaitu jika garis lebih panjang dari bsize
. Anda dapat memeriksanya dengan if(buffer[strlen(buffer)-1] != '\n')
. Jika Anda ingin menghapus baris baru, Anda dapat melakukannya dengan buffer[strcspn(buffer, "\n")] = 0
.
Secara umum, saya akan menyarankan untuk tidak mengharapkan pengguna untuk memasukkan input dalam beberapa format aneh yang harus Anda parsing ke variabel yang berbeda. Jika Anda ingin menetapkan variabel height
dan width
, jangan meminta keduanya sekaligus. Izinkan pengguna menekan enter di antara mereka. Juga, pendekatan ini sangat alami di satu sisi. Anda tidak akan pernah mendapatkan input dari stdin
sampai Anda menekan enter, jadi mengapa tidak selalu membaca seluruh baris? Tentu saja hal ini masih dapat menimbulkan masalah jika saluran lebih panjang dari buffer. Apakah saya ingat menyebutkan bahwa input pengguna kikuk di C? :)
Untuk menghindari masalah dengan garis yang lebih panjang dari buffer, Anda dapat menggunakan fungsi yang secara otomatis mengalokasikan buffer dengan ukuran yang sesuai, Anda bisa menggunakannya getline()
. Kekurangannya adalah bahwa Anda perlu free
hasil setelah itu.
Meningkatkan permainan
Jika Anda serius membuat program dalam C dengan input pengguna, saya akan merekomendasikan melihat-lihat di perpustakaan seperti ncurses
. Karena dengan begitu Anda kemungkinan juga ingin membuat aplikasi dengan beberapa grafik terminal. Sayangnya, Anda akan kehilangan portabilitas jika melakukannya, tetapi ini memberi Anda kontrol input pengguna yang jauh lebih baik. Misalnya, ini memberi Anda kemampuan untuk membaca penekanan tombol secara instan alih-alih menunggu pengguna menekan enter.
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
tidak mendeteksi sebagai buruk teks trailing non-numerik.fgets()
dari"1 2 junk"
,if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) {
tidak melaporkan sesuatu yang salah dengan masukan meskipun itu memiliki "sampah".scanf
dimaksudkan untuk digunakan dengan data yang diformat sempurna. Tetapi itu pun tidak benar. Selain masalah dengan "sampah" seperti yang disebutkan oleh @ chux, ada juga fakta bahwa format seperti"%d %d %d"
senang membaca input dari satu, dua, atau tiga baris (atau bahkan lebih, jika ada campur tangan baris kosong), bahwa tidak ada cara untuk memaksa (mengatakan) input dua baris dengan melakukan sesuatu seperti"%d\n%d %d"
, dll.scanf
mungkin sesuai untuk input stream yang diformat , tetapi sama sekali tidak baik untuk apa pun berbasis garis.scanf
luar biasa ketika Anda tahu input Anda selalu terstruktur dan berperilaku baik. Jika tidak...IMO, berikut adalah masalah terbesar dengan
scanf
:Risiko buffer overflow - jika Anda tidak menentukan lebar bidang untuk
%s
dan%[
penentu konversi, Anda berisiko buffer overflow (mencoba membaca lebih banyak input daripada ukuran buffer yang ditahan). Sayangnya, tidak ada cara yang baik untuk menentukannya sebagai argumen (seperti halnya denganprintf
) - Anda harus meng-hardcode-nya sebagai bagian dari specifier konversi atau melakukan beberapa gangguan makro.Menerima input yang harus ditolak - Jika Anda membaca input dengan
%d
specifier konversi dan Anda mengetik sesuatu seperti12w4
, Anda akan mengharapkanscanf
untuk menolak input itu, tetapi tidak - itu berhasil mengubah dan menetapkan12
, meninggalkanw4
aliran input untuk mengacaukan pembacaan selanjutnya.Jadi, apa yang sebaiknya Anda gunakan?
Saya biasanya merekomendasikan membaca semua input interaktif sebagai teks
fgets
- ini memungkinkan Anda menentukan jumlah karakter maksimum untuk dibaca sekaligus, sehingga Anda dapat dengan mudah mencegah buffer overflow:Satu kekhasan dari itu
fgets
adalah bahwa ia akan menyimpan baris tambahan di buffer jika ada ruang, sehingga Anda dapat melakukan pemeriksaan mudah untuk melihat apakah seseorang mengetik lebih banyak input daripada yang Anda harapkan:Bagaimana Anda menangani hal itu terserah Anda - Anda bisa menolak seluruh input dari tangan, dan menyeruput setiap input yang tersisa dengan
getchar
:Atau Anda dapat memproses input yang Anda dapatkan sejauh ini dan membaca lagi. Itu tergantung pada masalah yang Anda coba selesaikan.
Untuk tokenize input (membaginya berdasarkan pada satu atau lebih pembatas), Anda dapat menggunakan
strtok
, tetapi berhati-hatilah -strtok
memodifikasi inputnya (itu menimpa pembatas dengan string terminator), dan Anda tidak dapat mempertahankan statusnya (yaitu, Anda dapat ' t tokenize sebagian string, kemudian mulai tokenize yang lain, lalu ambil di mana Anda tinggalkan di string asli). Ada varian,,strtok_s
yang mempertahankan status tokenizer, tetapi AFAIK implementasinya adalah opsional (Anda harus memeriksa yang__STDC_LIB_EXT1__
ditentukan untuk melihat apakah itu tersedia).Setelah Anda tokenized input Anda, jika Anda perlu mengkonversi string ke angka (yaitu,
"1234"
=>1234
), Anda memiliki opsi.strtol
danstrtod
akan mengonversi representasi string dari bilangan bulat dan bilangan real ke jenisnya masing-masing. Mereka juga memungkinkan Anda menangkap12w4
masalah yang saya sebutkan di atas - salah satu argumen mereka adalah penunjuk ke karakter pertama yang tidak dikonversi dalam string:sumber
%*[%\n]
, Yang berguna untuk berurusan dengan garis yang terlalu panjang nanti dalam jawabannya).snprintf()
),.isspace()
hal itu - ia menerima karakter yang tidak ditandai yang diwakiliint
, jadi Anda perlu melakukan castingunsigned char
untuk menghindari UB pada platform tempatchar
ditandatangani.Dalam jawaban ini saya akan berasumsi bahwa Anda membaca dan menafsirkan baris teks . Mungkin Anda mendorong pengguna, yang sedang mengetik sesuatu dan menekan RETURN. Atau mungkin Anda sedang membaca baris teks terstruktur dari beberapa file data.
Karena Anda membaca baris teks, masuk akal untuk mengatur kode Anda di sekitar fungsi perpustakaan yang membaca, yah, baris teks. Fungsi Standar adalah
fgets()
, meskipun ada yang lain (termasukgetline
). Dan kemudian langkah selanjutnya adalah menafsirkan baris teks itu entah bagaimana.Inilah resep dasar untuk menelepon
fgets
untuk membaca satu baris teks:Ini cukup dibaca dalam satu baris teks dan mencetaknya kembali. Seperti yang tertulis itu memiliki beberapa keterbatasan, yang akan kita bahas sebentar lagi. Ini juga memiliki fitur yang sangat hebat: angka 512 yang kami berikan sebagai argumen kedua
fgets
adalah ukuran array yangline
kami mintafgets
untuk dibaca. Fakta ini - yang bisa kita katakanfgets
seberapa banyak itu diperbolehkan untuk dibaca - berarti kita dapat yakin bahwafgets
tidak akan meluap array dengan membaca terlalu banyak ke dalamnyaJadi sekarang kita tahu cara membaca satu baris teks, tetapi bagaimana jika kita benar-benar ingin membaca integer, atau angka floating-point, atau satu karakter, atau satu kata? (Artinya, bagaimana jika
scanf
panggilan kita mencoba untuk memperbaiki telah menggunakan format specifier seperti%d
,%f
,%c
, atau%s
?)Sangat mudah untuk menginterpretasikan ulang baris teks - string - sebagai salah satu dari hal-hal ini. Untuk mengonversi string menjadi integer, cara paling sederhana (meskipun tidak sempurna) untuk melakukannya adalah dengan menelepon
atoi()
. Untuk mengonversi ke angka floating-point, adaatof()
. (Dan ada juga cara yang lebih baik, seperti yang akan kita lihat sebentar lagi.) Berikut ini contoh yang sangat sederhana:Jika Anda ingin pengguna mengetik satu karakter (mungkin
y
ataun
sebagai jawaban ya / tidak), Anda dapat langsung mengambil karakter pertama dari baris tersebut, seperti ini:(Ini tentu saja mengabaikan kemungkinan bahwa pengguna mengetik respons multi-karakter; secara diam-diam mengabaikan setiap karakter tambahan yang diketik.)
Akhirnya, jika Anda ingin pengguna mengetikkan string jelas tidak mengandung spasi, jika Anda ingin memperlakukan jalur input
sebagai string
"hello"
diikuti oleh sesuatu yang lain (yang adalahscanf
format apa yang%s
akan dilakukan), yah, dalam hal ini, saya berselingkuh sedikit, itu tidak begitu mudah untuk menafsirkan ulang garis dengan cara itu, setelah semua, jadi jawaban untuk itu bagian dari pertanyaan harus menunggu sebentar.Tetapi pertama-tama saya ingin kembali ke tiga hal yang saya lewati.
(1) Kami sudah menelepon
untuk membaca ke dalam array
line
, dan di mana 512 adalah ukuran arrayline
jadifgets
tahu untuk tidak meluapnya. Tetapi untuk memastikan bahwa 512 adalah angka yang tepat (terutama, untuk memeriksa apakah mungkin seseorang mengubah program untuk mengubah ukuran), Anda harus membaca kembali ke manaline
pun dinyatakan. Itu merepotkan, jadi ada dua cara yang jauh lebih baik untuk menjaga ukuran tetap sinkron. Anda bisa, (a) menggunakan preprocessor untuk membuat nama untuk ukuran:Atau, (b) gunakan
sizeof
operator C :(2) Masalah kedua adalah bahwa kami belum memeriksa kesalahan. Saat Anda membaca input, Anda harus selalu memeriksa kemungkinan kesalahan. Jika karena alasan apa pun
fgets
tidak dapat membaca baris teks yang Anda minta, itu menunjukkan ini dengan mengembalikan pointer nol. Jadi kita seharusnya melakukan hal-hal sepertiAkhirnya, ada masalah bahwa untuk membaca satu baris teks,
fgets
membaca karakter dan mengisinya ke dalam array Anda sampai menemukan\n
karakter yang mengakhiri baris, dan itu mengisi\n
karakter ke dalam array Anda juga . Anda dapat melihat ini jika Anda sedikit memodifikasi contoh kami sebelumnya:Jika saya menjalankan ini dan ketik "Steve" ketika diminta, itu akan dicetak
Itu
"
pada baris kedua adalah karena string yang dibacanya dan dicetak kembali sebenarnya"Steve\n"
.Kadang-kadang baris baru tambahan itu tidak masalah (seperti ketika kita menelepon
atoi
atauatof
, karena mereka berdua mengabaikan input non-numerik tambahan setelah nomor), tetapi kadang-kadang itu sangat berarti. Sering kali kita ingin menghilangkan baris baru itu. Ada beberapa cara untuk melakukan itu, yang akan saya bahas sebentar lagi. (Aku tahu aku sudah mengatakan itu banyak. Tapi aku akan kembali ke semua hal itu, aku janji.)Pada titik ini, Anda mungkin berpikir: "Saya pikir Anda mengatakan
scanf
itu tidak baik, dan cara lain ini akan jauh lebih baik. Tetapifgets
mulai terlihat seperti gangguan. Memanggilscanf
itu mudah ! Tidak bisakah saya tetap menggunakannya? "Tentu, Anda bisa terus menggunakan
scanf
, jika mau. (Dan untuk hal-hal yang sangat sederhana, dalam beberapa hal itu lebih sederhana.) Tapi, tolong, jangan datang menangis kepada saya ketika itu membuat Anda gagal karena salah satu dari 17 keanehan dan kelemahannya, atau masuk ke loop tak terhingga karena memasukkan Anda tidak berharap, atau ketika Anda tidak tahu cara menggunakannya untuk melakukan sesuatu yang lebih rumit. Dan mari kita lihatfgets
gangguan yang sebenarnya:Anda selalu harus menentukan ukuran array. Yah, tentu saja, itu sama sekali bukan gangguan - itu fitur, karena buffer overflow adalah Hal yang Sangat Buruk.
Anda harus memeriksa nilai kembali. Sebenarnya, itu adalah pencucian, karena untuk menggunakannya
scanf
dengan benar, Anda harus memeriksa nilai pengembaliannya juga.Anda harus melepaskan bagian
\n
belakangnya. Saya akui, ini benar-benar gangguan. Saya berharap ada fungsi standar yang bisa saya tunjukkan kepada Anda yang tidak memiliki masalah kecil ini. (Tolong tidak ada yang mengemukakangets
.) Tetapi dibandingkan denganscanf's
17 gangguan yang berbeda, saya akan mengambil gangguan yang satu inifgets
setiap hari.Jadi bagaimana cara Anda strip baris baru itu? Tiga jalan:
(a) Cara yang jelas:
(B) Cara rumit & kompak:
Sayangnya yang ini tidak selalu berhasil.
(C) Cara lain kompak dan agak tidak jelas:
Dan sekarang setelah keluar dari jalan, kita dapat kembali ke hal lain yang saya lewatkan: ketidaksempurnaan
atoi()
danatof()
. Masalahnya adalah mereka tidak memberi Anda indikasi sukses atau gagal: mereka diam-diam mengabaikan input nonnumerik, dan mereka diam-diam mengembalikan 0 jika tidak ada input numerik sama sekali. Alternatif yang lebih disukai - yang juga memiliki kelebihan lain - adalahstrtol
danstrtod
.strtol
juga memungkinkan Anda menggunakan basis selain 10, artinya Anda bisa mendapatkan efek (antara lain)%o
atau%x
denganscanf
. Tetapi menunjukkan bagaimana menggunakan fungsi-fungsi ini dengan benar adalah cerita itu sendiri, dan akan menjadi terlalu banyak gangguan dari apa yang sudah berubah menjadi narasi yang cukup terfragmentasi, jadi saya tidak akan mengatakan apa-apa lagi tentang mereka sekarang.Sisa dari narasi utama menyangkut input yang mungkin Anda coba uraikan yang lebih rumit daripada hanya satu angka atau karakter. Bagaimana jika Anda ingin membaca baris yang berisi dua angka, atau beberapa kata yang dipisahkan spasi, atau tanda baca framing tertentu? Di situlah hal-hal menjadi menarik, dan di mana hal-hal itu mungkin menjadi rumit jika Anda mencoba melakukan hal-hal menggunakan
scanf
, dan di mana ada jauh lebih banyak opsi sekarang bahwa Anda telah membaca satu baris teks dengan bersihfgets
, meskipun cerita lengkap tentang semua opsi tersebut mungkin bisa mengisi buku, jadi kita hanya akan bisa menggaruk permukaan di sini.Teknik favorit saya adalah memecah garis menjadi "kata-kata" yang dipisahkan oleh spasi, kemudian melakukan sesuatu lebih jauh dengan setiap "kata". Salah satu fungsi Standar utama untuk melakukan ini adalah
strtok
(yang juga memiliki masalah, dan yang juga menilai seluruh diskusi terpisah). Preferensi saya sendiri adalah fungsi khusus untuk membangun array pointer ke setiap "kata" yang terpisah, sebuah fungsi yang saya jelaskan dalam catatan kursus ini . Bagaimanapun, setelah Anda mendapatkan "kata-kata", Anda dapat memproses lebih lanjut masing-masing, mungkin dengan fungsi yang samaatoi
/atof
/strtol
/strtod
kita sudah melihat.Paradoksnya, meskipun kita telah menghabiskan cukup banyak waktu dan upaya di sini untuk mencari tahu bagaimana cara menjauh
scanf
, cara lain yang baik untuk berurusan dengan baris teks yang baru saja kita bacafgets
adalah dengan meneruskannyasscanf
. Dengan cara ini, Anda berakhir dengan sebagian besar keuntunganscanf
, tetapi tanpa sebagian besar kerugian.Jika sintaks input Anda sangat rumit, mungkin perlu menggunakan pustaka "regexp" untuk menguraikannya.
Terakhir, Anda dapat menggunakan solusi parsing ad hoc apa pun yang cocok untuk Anda. Anda dapat bergerak melalui garis karakter pada suatu waktu dengan
char *
pointer memeriksa karakter yang Anda harapkan. Atau Anda dapat mencari karakter tertentu menggunakan fungsi sepertistrchr
ataustrrchr
, ataustrspn
ataustrcspn
, ataustrpbrk
. Atau Anda dapat mem-parsing / mengonversi dan melewati kelompok karakter digit menggunakanstrtol
ataustrtod
fungsi yang kami lewati sebelumnya.Jelas ada banyak lagi yang bisa dikatakan, tapi mudah-mudahan pengantar ini akan membantu Anda memulai.
sumber
sizeof (line)
daripada sekadarsizeof line
? Yang pertama membuatnya terlihat sepertiline
nama tipe!sscanf
sebagai mesin konversi tetapi mengumpulkan (dan mungkin memijat) input dengan alat yang berbeda. Tapi mungkin layak disebutkangetline
dalam konteks itu.fscanf
gangguan aktual", maksud Andafgets
? Dan gangguan # 3 benar-benar membuatku jengkel, terutama mengingat bahwascanf
mengembalikan pointer yang tidak berguna ke buffer daripada mengembalikan jumlah input karakter (yang akan membuat pengupasan baris baru jauh lebih bersih).sizeof
gaya Anda . Bagi saya, mengingat ketika Anda melihat parens itu mudah: Saya anggap(type)
seperti pemain tanpa nilai (karena kami hanya tertarik pada jenisnya). Satu hal lagi: Anda mengatakan itustrtok(line, "\n")
tidak selalu berhasil, tetapi tidak jelas kapan itu mungkin tidak. Saya kira Anda berpikir tentang kasus di mana garis lebih panjang dari buffer, jadi kami tidak memiliki baris baru, danstrtok()
mengembalikan nol? Sangat disayangkanfgets()
tidak mengembalikan nilai yang lebih berguna sehingga kita bisa tahu apakah baris baru ada atau tidak.Alih-alih
scanf(some_format, ...)
, pertimbangkanfgets()
dengansscanf(buffer, some_format_and %n, ...)
Dengan menggunakan
" %n"
, kode dapat dengan mudah mendeteksi jika semua format berhasil dipindai dan tidak ada sampah non-spasi putih di akhir.sumber
Mari kita nyatakan persyaratan parsing sebagai:
input yang valid harus diterima (dan dikonversi ke bentuk lain)
input yang tidak valid harus ditolak
ketika input apa pun ditolak, maka perlu untuk memberikan pengguna dengan pesan deskriptif yang menjelaskan (secara jelas "mudah dimengerti oleh orang normal yang bukan pemrogram" bahasa) mengapa itu ditolak (sehingga orang dapat mencari cara untuk memperbaiki masalah)
Untuk menjaga hal-hal yang sangat sederhana, mari kita mempertimbangkan penguraian bilangan bulat desimal tunggal (yang diketik oleh pengguna) dan tidak ada yang lain. Kemungkinan alasan input pengguna untuk ditolak adalah:
Mari kita juga mendefinisikan "input berisi karakter yang tidak dapat diterima" dengan benar; dan katakan itu:
5" akan diperlakukan sebagai "5")
Dari ini kita dapat menentukan bahwa pesan kesalahan berikut diperlukan:
Dari titik ini kita dapat melihat bahwa fungsi yang cocok untuk mengubah string menjadi integer perlu membedakan antara jenis kesalahan yang sangat berbeda; dan sesuatu seperti "
scanf()
" atau "atoi()
" atau "strtoll()
" sama sekali dan sama sekali tidak berharga karena mereka gagal memberi Anda indikasi apa pun yang salah dengan input (dan menggunakan definisi yang benar-benar tidak relevan dan tidak tepat tentang apa yang / tidak "valid" memasukkan").Sebagai gantinya, mari mulai menulis sesuatu yang tidak berguna:
Untuk memenuhi persyaratan yang dinyatakan; ini
convertStringToInteger()
fungsi kemungkinan akan berakhir menjadi beberapa ratus baris kode dengan sendirinya.Sekarang, ini hanya "parsing bilangan bulat desimal tunggal". Bayangkan jika Anda ingin menguraikan sesuatu yang kompleks; seperti daftar struktur "nama, alamat jalan, nomor telepon, alamat email"; atau mungkin seperti bahasa pemrograman. Untuk kasus ini, Anda mungkin perlu menulis ribuan baris kode untuk membuat parse yang bukan lelucon lumpuh.
Dengan kata lain...
Tulis sendiri (berpotensi ribuan baris) kode, sesuai dengan kebutuhan Anda.
sumber
Berikut adalah contoh penggunaan
flex
untuk memindai input sederhana, dalam hal ini file angka floating point ASCII yang mungkin dalam format US (n,nnn.dd
) atau Eropa (n.nnn,dd
). Ini hanya disalin dari program yang jauh lebih besar, jadi mungkin ada beberapa referensi yang tidak terselesaikan:sumber
Jawaban lain memberikan perincian tingkat rendah yang tepat, jadi saya akan membatasi diri ke tingkat yang lebih tinggi: Pertama, analisis seperti apa tampilan setiap garis input. Cobalah untuk mendeskripsikan input dengan sintaks formal - jika beruntung, Anda akan menemukannya dapat dijelaskan menggunakan tata bahasa biasa , atau setidaknya tata bahasa bebas konteks . Jika tata bahasa biasa sudah mencukupi, maka Anda dapat membuat kode a finite-stateyang mengenali dan menafsirkan setiap karakter baris perintah satu per satu. Kode Anda kemudian akan membaca baris (seperti yang dijelaskan dalam balasan lain), kemudian memindai karakter di buffer melalui mesin negara. Di negara bagian tertentu Anda berhenti dan mengonversi media yang dipindai sejauh ini ke angka atau apa pun. Anda mungkin dapat 'roll your own' jika ini sederhana; jika Anda membutuhkan tata bahasa bebas konteks lengkap, Anda lebih baik mencari tahu cara menggunakan alat parsing yang ada (ulang:
lex
danyacc
atau variannya).sumber
errno == EOVERFLOW
setelah menggunakanstrtoll
) dimungkinkan.