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 (termasuk getline
). Dan kemudian langkah selanjutnya adalah menafsirkan baris teks itu entah bagaimana.
Inilah resep dasar untuk menelepon fgets
untuk membaca satu baris teks:
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
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 yang
line
kami minta fgets
untuk dibaca. Fakta ini - yang bisa kita katakan fgets
seberapa banyak itu diperbolehkan untuk dibaca - berarti kita dapat yakin bahwa fgets
tidak akan meluap array dengan membaca terlalu banyak ke dalamnya
Jadi 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, ada atof()
. (Dan ada juga cara yang lebih baik, seperti yang akan kita lihat sebentar lagi.) Berikut ini contoh yang sangat sederhana:
printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);
Jika Anda ingin pengguna mengetik satu karakter (mungkin y
atau
n
sebagai jawaban ya / tidak), Anda dapat langsung mengambil karakter pertama dari baris tersebut, seperti ini:
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
(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
hello world!
sebagai string "hello"
diikuti oleh sesuatu yang lain (yang adalah scanf
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
fgets(line, 512, stdin);
untuk membaca ke dalam array line
, dan di mana 512 adalah ukuran array line
jadi fgets
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 mana line
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:
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
Atau, (b) gunakan sizeof
operator C :
fgets(line, sizeof(line), stdin);
(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 seperti
printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
printf("Well, never mind, then.\n");
exit(1);
}
Akhirnya, 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:
printf("you typed: \"%s\"\n", line);
Jika saya menjalankan ini dan ketik "Steve" ketika diminta, itu akan dicetak
you typed: "Steve
"
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
atau atof
, 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. Tetapi fgets
mulai terlihat seperti gangguan. Memanggil scanf
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 lihat fgets
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 mengemukakan gets
.) Tetapi dibandingkan dengan scanf's
17 gangguan yang berbeda, saya akan mengambil gangguan yang satu ini fgets
setiap hari.
Jadi bagaimana cara Anda strip baris baru itu? Tiga jalan:
(a) Cara yang jelas:
char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';
(B) Cara rumit & kompak:
strtok(line, "\n");
Sayangnya yang ini tidak selalu berhasil.
(C) Cara lain kompak dan agak tidak jelas:
line[strcspn(line, "\n")] = '\0';
Dan sekarang setelah keluar dari jalan, kita dapat kembali ke hal lain yang saya lewatkan: ketidaksempurnaan atoi()
dan atof()
. 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 - adalah strtol
dan strtod
.
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 bersih fgets
, 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 sama atoi
/ 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 baca
fgets
adalah dengan meneruskannya sscanf
. Dengan cara ini, Anda berakhir dengan sebagian besar keuntungan scanf
, 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 seperti strchr
atau strrchr
, atau strspn
atau strcspn
, atau strpbrk
. Atau Anda dapat mem-parsing / mengonversi dan melewati kelompok karakter digit menggunakan strtol
atau
strtod
fungsi yang kami lewati sebelumnya.
Jelas ada banyak lagi yang bisa dikatakan, tapi mudah-mudahan pengantar ini akan membantu Anda memulai.
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
tidak mendeteksi sebagai buruk teks trailing non-numerik.