Topik ini membahas masukan dan keluaran (input/output, disingkat I/O) pada tingkat dasar, yaitu membaca data dari pengguna melalui std::cin, menulis keluaran biasa melalui std::cout, melaporkan error melalui std::cerr, dan mengatur tampilan dengan manipulator. Setiap program membutuhkan masukan saat berjalan, sehingga seluruhnya dijalankan lewat shell dengan masukan yang dialihkan agar keluarannya dapat direproduksi.
Membaca masukan dengan std::cin#
std::cin adalah stream masukan standar, yaitu sumber data yang lazimnya keyboard, sedangkan operator >> mengambil data dari stream dan menyimpannya ke dalam variabel. Untuk satu kata, std::cin >> nama sudah memadai, sedangkan teks yang mengandung spasi dibaca dengan std::getline karena operator >> berhenti pada spasi pertama.
// tidak-dijalankan (butuh masukan pengguna saat berjalan; dijalankan lewat bash di bawah)
#include <iostream>
#include <string>
int main() {
std::string nama;
int umur = 0;
std::cout << "Masukkan nama panggilan: ";
std::getline(std::cin, nama);
std::cout << "Masukkan umur: ";
std::cin >> umur;
std::cout << "Halo, " << nama << "! Tahun depan usia menjadi " << umur + 1 << ".\n";
return 0;
}Program ini dibangun, lalu dijalankan dengan masukan yang dialihkan melalui printf. Perintah printf "Citra\n17\n" memproduksi dua baris, yaitu Citra dan 17, sehingga program menerima masukan seolah diketik pengguna.
# kompilasi program salam
g++ salam.cpp -o /tmp/salam
# jalankan dengan masukan yang dialihkan dari printf
printf "Citra\n17\n" | /tmp/salamMasukkan nama panggilan: Masukkan umur: Halo, Citra! Tahun depan usia menjadi 18.Prompt dan jawaban tampak berdempetan dalam satu baris karena masukan dialihkan. Saat diketik di terminal, penekanan Enter ikut tercetak dan memindahkan tampilan ke baris baru. Pada pengalihan masukan, tidak ada tekanan yang tercetak, sehingga keluaran aslinya memang satu baris seperti di atas. Di terminal yang berjalan interaktif, dialog yang terlihat tetap rapi baris demi baris.
Memvalidasi masukan pengguna#
Masukan pengguna tidak dapat diasumsikan benar, sehingga keberhasilan pembacaan wajib diperiksa. Operator std::cin >> nilai meletakkan stream dalam keadaan gagal apabila isinya tidak sesuai tipe variabel, dan keadaan tersebut dapat diperiksa langsung dalam kondisi if. Ketika kegagalan itu terjadi, pesan error dilaporkan lewat std::cerr, yaitu stream khusus error yang tidak dibuffer dan mengalir melalui jalur stderr.
// tidak-dijalankan (butuh masukan pengguna saat berjalan; dijalankan lewat bash di bawah)
#include <iostream>
int main() {
int nilai = 0;
std::cout << "Masukkan sebuah bilangan bulat: ";
if (!(std::cin >> nilai)) {
std::cerr << "Error: masukan bukan bilangan bulat yang valid.\n";
return 1;
}
std::cout << "Kuadrat dari " << nilai << " adalah " << nilai * nilai << ".\n";
return 0;
}Program berikut dijalankan dua kali pada perintah yang sama, pertama dengan masukan yang valid, lalu dengan teks yang bukan bilangan bulat.
# kompilasi program validasi
g++ validasi.cpp -o /tmp/validasi
# jalankan dengan masukan yang valid
printf "12\n" | /tmp/validasi
# jalankan dengan masukan yang tidak valid
printf "dua belas\n" | /tmp/validasiMasukkan sebuah bilangan bulat: Kuadrat dari 12 adalah 144.
Masukkan sebuah bilangan bulat: Error: masukan bukan bilangan bulat yang valid.Pada percobaan kedua, std::cin gagal membaca teks dua belas sebagai bilangan bulat, sehingga jalur error berjalan dan program mengembalikan kode keluar 1. Kode keluar itu dapat diperiksa dengan echo $?, dan nilainya memungkinkan script shell menentukan langkah berikutnya tanpa membaca pesannya. Pola if (!(std::cin >> nilai)) menjadi kebiasaan yang disarankan untuk setiap pembacaan masukan pengguna.
Mengalihkan keluaran dan error di shell#
Pemisahan std::cout dan std::cerr menjadi berguna penuh di shell, karena kedua alirannya dapat dialihkan ke tempat berbeda. Simbol > mengalihkan keluaran standar, sedangkan 2> mengalihkan jalur stderr, dan keduanya dapat digunakan sekaligus pada satu perintah. Contoh berikut menjalankan validasi dengan masukan yang tidak valid, lalu memisahkan kedua keluarannya ke berkas.
# kompilasi ulang program validasi agar siap dipakai
g++ validasi.cpp -o /tmp/validasi
# jalankan program dengan kedua aliran keluaran dipisah
printf "dua belas\n" | /tmp/validasi > /tmp/keluaran.txt 2> /tmp/error.txt
# tampilkan isi berkas keluaran standar
echo "isi keluaran.txt: [$(cat /tmp/keluaran.txt)]"
# tampilkan isi berkas error
echo "isi error.txt: [$(cat /tmp/error.txt)]"isi keluaran.txt: [Masukkan sebuah bilangan bulat: ]
isi error.txt: [Error: masukan bukan bilangan bulat yang valid.]Prompt yang dicetak oleh std::cout berpindah ke /tmp/keluaran.txt, sedangkan pesan error milik std::cerr berpindah ke /tmp/error.txt. Dengan pemisahan ini, script shell dapat mencatat keluaran normal ke satu berkas sambil menyalurkan laporan error ke tempat lain, misalnya log khusus. Aliran std::cerr sengaja tidak dibuffer agar pesan error tetap muncul segera meskipun keluaran normal sedang dialihkan.
Membaca angka lalu teks dalam satu program#
Kebalikan urutan pada salam.cpp juga sering dibutuhkan, yaitu membaca angka dengan >> terlebih dahulu, lalu membaca baris teks dengan std::getline. Sisa karakter baris baru dari pembacaan >> harus dibuang terlebih dahulu dengan std::cin.ignore(), karena karakter itulah yang akan dibaca std::getline sebagai baris kosong. Program berikut menerapkan polanya pada pencatatan pesanan sederhana.
// tidak-dijalankan (butuh masukan pengguna saat berjalan; dijalankan lewat bash di bawah)
#include <iostream>
#include <string>
int main() {
int nomorMeja = 0;
std::string catatan;
std::cout << "Nomor meja: ";
std::cin >> nomorMeja;
std::cin.ignore(); // buang sisa baris baru di buffer
std::cout << "Catatan pesanan: ";
std::getline(std::cin, catatan);
std::cout << "Meja " << nomorMeja << " mencatat: " << catatan << "\n";
return 0;
}# kompilasi program pencatatan pesanan
g++ sisa.cpp -o /tmp/sisa
# masukan angka lalu teks yang mengandung spasi
printf "5\nkopi tanpa gula\n" | /tmp/sisaNomor meja: Catatan pesanan: Meja 5 mencatat: kopi tanpa gulaTanpa std::cin.ignore(), keluarannya menjadi Meja 5 mencatat: dengan baris kosong, karena teks yang sebenarnya dibaca std::getline adalah sisa baris baru. Pemanggilan std::cin.ignore() tanpa argumen membuang satu karakter, yang sudah memadai untuk pola ini. Bentuk yang lebih ketat menerima jumlah karakter dan karakter penghenti sebagai argumennya, tetapi bentuk tanpa argumen menjadi titik awal yang cukup.
Mengatur tampilan dengan manipulator#
Manipulator adalah fungsi yang disisipkan ke dalam stream untuk mengubah tampilan data berikutnya. Beberapa yang paling sering dipakai adalah std::fixed dan std::setprecision untuk angka pecahan, std::setw untuk lebar kolom, serta std::boolalpha untuk mencetak true dan false sebagai teks. Manipulator yang berlaku untuk angka berasal dari header <iomanip>.
#include <iomanip>
#include <iostream>
int main() {
double pi = 3.14159265;
std::cout << pi << "\n"; // presisi bawaan
std::cout << std::fixed << std::setprecision(2) << pi << "\n"; // dua angka di belakang koma
std::cout << std::setw(10) << 42 << "|\n"; // rata kanan, lebar 10
std::cout << std::boolalpha << (3 > 2) << "\n"; // cetak true/false
return 0;
}3.14159
3.14
42|
trueTampilan bawaan mencetak 3.14159 dengan enam angka penting, sedangkan kombinasi std::fixed dan std::setprecision(2) menetapkannya menjadi 3.14. Manipulator std::setw(10) menyediakan lebar sepuluh karakter dan menyelaraskan angka ke kanan, sehingga tampak delapan spasi sebelum angka 42. Perlu dicatat bahwa std::setw hanya berlaku untuk satu data berikutnya, sedangkan pengaturan presisi dan std::boolalpha berlaku terus sampai diubah lagi.
Kesalahan umum#
Kekeliruan berikut paling sering muncul ketika program mulai menerima masukan.
- Mencampur
std::cin >>danstd::getlinetanpa membersihkan sisa baris. Pembacaan dengan>>meninggalkan karakter baris baru di buffer, sehinggastd::getlineberikutnya mengembalikan teks kosong. Perbaikannya memanggilstd::cin.ignore()untuk membuang sisa baris sebelumstd::getline. - Memakai nilai hasil pembacaan tanpa memeriksa keberhasilannya. Apabila
std::cingagal, variabel diberi nilai 0 dan stream tetap dalam keadaan gagal sehingga pembacaan berikutnya ikut diloncati. Perbaikannya memeriksa kondisistd::cinsebelum nilai dipakai. - Menggunakan
std::endlpada setiap baris keluaran. Manipulator tersebut memulai baris baru sekaligus mengosongkan buffer setiap kali dipanggil, sedangkan"\n"memulai baris baru tanpa pengosongan sehingga lebih ringan untuk keluaran yang banyak. - Melaporkan error melalui
std::cout. Pesan error tercampur dengan keluaran normal, dan pengalihan>pada shell memindahkan keduanya ke tempat yang sama sehingga tidak dapat dibedakan. Perbaikannya melaporkan kegagalan melaluistd::cerr, sebagaimana dipraktikkan pada bagian validasi.
Ringkasan#
std::cinmembaca masukan standar dengan operator>>untuk satu data berformat, sedangkanstd::getlinemembaca satu baris penuh termasuk spasinya.- Keberhasilan pembacaan diperiksa lewat kondisi stream, dan kegagalan dilaporkan melalui
std::cerrbeserta kode keluar selain nol. - Manipulator seperti
std::fixed,std::setprecision,std::setw, danstd::boolalphamengatur tampilan keluaran tanpa mengubah nilainya. - Sisa baris baru setelah
>>harus dibuang denganstd::cin.ignore()sebelumstd::getlinedipakai.
Referensi#
- std::cin — cppreference — Dokumentasi stream masukan standar beserta keadaan errornya.
- std::cerr — cppreference — Dokumentasi stream error yang tidak dibuffer.
- Input/output manipulators — cppreference — Daftar manipulator standar beserta efeknya pada stream.
- 1.5 — Introduction to iostream: cout, cin, and endl — LearnCpp — Pengenalan iostream beserta pemakaian cout dan cin.
- std::cin and handling invalid input — LearnCpp — Pola pembacaan masukan dan penanganan nilai yang tidak valid.