Mengolah File dan Menangani Kesalahan
Bagaimana membuat program yang dapat menyimpan data agar tidak hilang ketika program ditutup? Bagaimana pula memastikan program tetap dapat merespons ketika terjadi kesalahan? Pelajari cara membaca, menulis, dan mengelola file serta menangani kesalahan dalam C++.
Bayangkan kita sedang membuat program untuk mengelola nilai murid. Program tersebut dapat menerima nama, nilai tugas, nilai ujian, menghitung rata-rata, dan menentukan apakah seorang murid dinyatakan lulus.
Program berjalan dengan baik. Namun, ada satu masalah: ketika program ditutup, seluruh data yang telah dimasukkan akan hilang.
Mengapa demikian? Karena selama ini kita menyimpan data menggunakan variabel di dalam memori komputer. Ketika program selesai dijalankan, memori yang digunakan oleh program akan dilepaskan. Data yang hanya tersimpan di sana tidak otomatis menjadi dokumen permanen.
Padahal, dalam aplikasi nyata, kita sering membutuhkan data yang dapat digunakan kembali. Misalnya, data murid harus tetap tersedia meskipun aplikasi ditutup dan dibuka kembali keesokan harinya.
Untuk mengatasi masalah tersebut, program dapat menyimpan data ke dalam file. Dengan begitu, informasi tidak hanya berada di memori ketika program berjalan, tetapi juga dapat disimpan pada media penyimpanan komputer.
Namun, proses pengolahan file tidak selalu berjalan sesuai harapan. File yang ingin dibaca mungkin tidak ditemukan, lokasi penyimpanan mungkin tidak tersedia, atau format data yang dibaca mungkin tidak sesuai. Karena itu, kita juga perlu mempelajari bagaimana program mendeteksi dan menangani kesalahan.
Dalam materi ini, kita akan mempelajari kedua kemampuan tersebut secara bertahap.
- Mengapa program membutuhkan file?
- Mengenal file stream dalam C++
- Menulis data ke dalam file
- Membaca data dari file
- Menambahkan data tanpa menghapus isi file
- Memahami mode pembukaan file
- Memeriksa keberhasilan operasi file
- Mengenal error handling
- Menggunakan try, throw, dan catch
- Memahami jenis kesalahan program
- Membuat program pengelolaan data
- Latihan dan tantangan
1. Mengapa Program Membutuhkan File?
Sebelum membahas kode C++, kita perlu memahami terlebih dahulu perbedaan antara data yang disimpan di memori dan data yang disimpan dalam file.
Ketika program dijalankan, variabel digunakan untuk menyimpan informasi yang dibutuhkan selama proses berlangsung. Misalnya, variabel nama menyimpan nama murid dan variabel nilai menyimpan nilai ujian.
Data tersebut dapat digunakan untuk berbagai operasi, tetapi keberadaannya bergantung pada masa hidup variabel dan program. Ketika program selesai, data di memori tidak otomatis tersimpan sebagai dokumen.
Sementara itu, file merupakan kumpulan data yang disimpan pada media penyimpanan, seperti SSD, hard disk, atau flashdisk. File dapat dibuka kembali oleh program lain maupun oleh program yang sama pada waktu berbeda.
Contohnya, sebuah aplikasi kasir dapat menggunakan variabel untuk menghitung total belanja. Setelah transaksi selesai, informasi transaksi tersebut dapat disimpan ke file agar dapat diperiksa kembali di kemudian hari.
Dalam C++, kita dapat melakukan operasi tersebut menggunakan fasilitas yang tersedia di standard library.
Sebelum melanjutkan, pastikan kita memahami konsep penyimpanan data dan variabel.
2. Mengenal File Stream dalam C++
Untuk mengolah file, C++ menyediakan library bernama fstream.
Library ini menyediakan beberapa jenis stream yang dapat digunakan untuk melakukan operasi file. Kata stream dapat dipahami sebagai aliran data antara program dan sumber atau tujuan data.
Ketika program membaca file, data mengalir dari file menuju program. Sebaliknya, ketika program menulis file, data mengalir dari program menuju file.
Terdapat tiga class utama yang perlu kita kenal:
| Class | Fungsi | Penggunaan |
|---|---|---|
ifstream |
Membaca data dari file. | Input file. |
ofstream |
Menulis data ke file. | Output file. |
fstream |
Membaca sekaligus menulis data. | Input dan output file. |
Untuk menggunakannya, kita perlu menyertakan library fstream di bagian awal program.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
Perhatikan bahwa kita tetap menggunakan iostream untuk menampilkan informasi ke layar dan menerima input dari keyboard. Sementara itu, fstream digunakan untuk melakukan operasi file.
Memahami alur file stream
Dengan memahami tiga class tersebut, kita sudah memiliki dasar untuk membuat program yang dapat menyimpan dan mengambil informasi dari file.
3. Menulis Data ke Dalam File
Mari kita mulai dengan contoh sederhana. Kita ingin membuat program yang menyimpan sebuah pesan ke dalam file bernama catatan.txt.
Untuk menulis data, kita menggunakan ofstream.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream file("catatan.txt");
file << "Belajar pemrograman C++" << endl;
file << "Materi file handling";
file.close();
cout << "Data berhasil ditulis.";
return 0;
}
Jika program berhasil dijalankan, file catatan.txt akan dibuat pada lokasi kerja program. File tersebut berisi:
Belajar pemrograman C++
Materi file handling
Mari kita pahami bagian penting dari kode tersebut.
ofstream file("catatan.txt");
Membuat objek stream bernama file untuk menulis data ke file catatan.txt. Jika file belum tersedia, operasi pembukaan dengan mode default output akan membuatnya.
file << "Belajar pemrograman C++";
Mengirimkan teks dari program ke dalam file menggunakan operator <<.
file.close();
Menutup file setelah operasi selesai.
Perlu diperhatikan bahwa ketika file dibuka menggunakan ofstream dengan mode default, isi file lama biasanya akan diganti. Jika file belum ada, file baru akan dibuat.
catatan.txt sebelumnya sudah memiliki data, pembukaan dengan mode output default dapat menghapus isi lama dan menggantinya dengan data baru. Jika ingin mempertahankan isi lama, kita perlu menggunakan mode penambahan data yang akan dibahas berikutnya.
Menulis data dari input pengguna
Program akan lebih berguna jika data yang disimpan berasal dari pengguna, bukan hanya teks yang sudah ditentukan di dalam kode.
Misalnya, kita ingin meminta nama dan umur pengguna, lalu menyimpannya ke dalam file.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
string nama;
int umur;
cout << "Masukkan nama: ";
getline(cin, nama);
cout << "Masukkan umur: ";
cin >> umur;
ofstream file("data.txt");
file << nama << endl;
file << umur << endl;
file.close();
cout << "Data berhasil disimpan.";
return 0;
}
Misalnya pengguna memasukkan nama Andi dan umur 17 tahun. Isi file data.txt akan menjadi:
Andi
17
Program tersebut sudah dapat menyimpan data ke file. Namun, bagaimana jika kita ingin menggunakan kembali data yang telah disimpan?
Untuk itu, kita perlu mempelajari cara membaca file.
4. Membaca Data dari File
Setelah data disimpan, kita tentu ingin dapat membukanya kembali. Misalnya, program pengelolaan murid harus dapat menampilkan nama dan umur yang sebelumnya sudah tersimpan.
Untuk membaca data, kita menggunakan ifstream.
Perhatikan contoh berikut:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream file("data.txt");
string nama;
int umur;
getline(file, nama);
file >> umur;
cout << "Nama: " << nama << endl;
cout << "Umur: " << umur << endl;
file.close();
return 0;
}
Jika file berisi nama Andi dan umur 17, hasilnya adalah:
Nama: Andi
Umur: 17
Perhatikan dua perintah untuk membaca data:
(- ) getline(file, nama); digunakan untuk membaca satu baris teks dari file.
(- ) file >> umur; digunakan untuk membaca nilai yang dapat diproses sebagai integer.
Perbedaan keduanya mirip dengan penggunaan getline() dan operator >> ketika menerima input dari keyboard.
Materi input dan output akan membantu memahami perbedaan penggunaan cin, getline(), dan operator stream.
Membaca seluruh isi file
Contoh sebelumnya membaca dua data dengan jumlah baris yang sudah diketahui. Namun, bagaimana jika isi file terdiri atas banyak baris dan jumlahnya tidak kita ketahui?
Kita dapat menggunakan perulangan untuk membaca file sampai tidak ada data lagi yang dapat dibaca.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream file("catatan.txt");
string baris;
while (getline(file, baris)) {
cout << baris << endl;
}
file.close();
return 0;
}
Setiap kali getline() berhasil membaca satu baris, perulangan akan dijalankan. Ketika sudah tidak ada baris yang dapat dibaca, kondisi perulangan menjadi salah dan program berhenti membaca.
Misalnya, file catatan.txt berisi:
Belajar C++
Mengenal file handling
Membaca data
Menulis data
Program akan menampilkan seluruh baris tersebut secara berurutan.
Pendalaman: mengapa tidak menggunakan while(!file.eof())?
Metode while (!file.eof()) sering ditemukan dalam contoh lama, tetapi bukan cara yang tepat untuk mengendalikan proses pembacaan.
Status EOF baru dapat diketahui setelah operasi pembacaan mencoba melewati akhir file. Akibatnya, pola tersebut dapat menyebabkan data terakhir diproses secara keliru atau menghasilkan pengulangan yang tidak diharapkan.
Lebih baik menggunakan kondisi operasi pembacaan secara langsung, seperti:
while (getline(file, baris)) {
cout << baris << endl;
}
5. Menambahkan Data Tanpa Menghapus Isi File
Bayangkan kita membuat aplikasi pencatatan transaksi. Setiap kali pengguna memasukkan transaksi baru, data tersebut harus ditambahkan ke catatan yang sudah ada.
Jika kita menggunakan ofstream dengan mode default, isi file lama dapat terhapus. Padahal, kita ingin mempertahankan seluruh transaksi sebelumnya.
Untuk mengatasi masalah tersebut, kita dapat menggunakan mode ios::app.
Mode ini membuat data baru ditambahkan di bagian akhir file.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
string transaksi;
cout << "Masukkan transaksi: ";
getline(cin, transaksi);
ofstream file("transaksi.txt", ios::app);
file << transaksi << endl;
file.close();
cout << "Transaksi berhasil ditambahkan.";
return 0;
}
Misalnya, isi awal file adalah:
Pembelian buku
Pembelian pensil
Kemudian pengguna memasukkan transaksi baru, yaitu Pembelian penghapus.
Setelah program dijalankan, isi file menjadi:
Pembelian buku
Pembelian pensil
Pembelian penghapus
Data lama tetap tersimpan, sedangkan data baru ditambahkan di bagian akhir.
ios::app ketika ingin menambahkan data baru tanpa mengganti isi file yang sudah ada, misalnya untuk catatan transaksi, riwayat aktivitas, atau daftar log.
6. Memahami Mode Pembukaan File
Selain ios::app, C++ menyediakan beberapa mode pembukaan file lainnya. Mode tersebut menentukan bagaimana file akan diperlakukan ketika dibuka.
| Mode | Fungsi |
|---|---|
ios::in |
Membuka file untuk membaca data. |
ios::out |
Membuka file untuk menulis data. Jika digunakan sendiri, umumnya isi lama akan dipotong. |
ios::app |
Menambahkan data di akhir file. Setiap penulisan diarahkan ke bagian akhir. |
ios::ate |
Membuka file dan memposisikan posisi awal operasi di akhir file. Posisi dapat dipindahkan setelahnya. |
ios::trunc |
Menghapus isi file ketika file dibuka untuk penulisan. |
ios::binary |
Membuka file dalam mode biner, bukan mode teks. |
Beberapa mode dapat digabungkan menggunakan operator OR bitwise |.
Contohnya:
fstream file("data.txt", ios::in | ios::out);
Kode tersebut membuka file untuk membaca dan menulis. Perlu diingat bahwa jika file belum tersedia, kombinasi tersebut tidak otomatis membuat file baru seperti pembukaan dengan ofstream biasa.
Pendalaman: apa perbedaan ios::app dan ios::ate?
Keduanya berkaitan dengan posisi akhir file, tetapi memiliki perilaku berbeda.
ios::app memastikan setiap operasi penulisan dilakukan di akhir file.
ios::ate hanya menempatkan posisi awal stream di akhir file ketika dibuka. Setelah itu, posisi dapat dipindahkan menggunakan operasi pencarian posisi seperti seekp().
Untuk kebutuhan sederhana menambahkan catatan di akhir file, biasanya kita menggunakan ios::app.
7. Memeriksa Keberhasilan Operasi File
Sampai di sini kita sudah bisa membaca dan menulis file. Namun, ada satu hal yang tidak boleh diabaikan: bagaimana jika file gagal dibuka?
Misalnya, kita ingin membaca file bernama data_murid.txt, tetapi file tersebut belum dibuat. Atau kita ingin menyimpan data ke lokasi yang tidak dapat diakses oleh program.
Jika kita tidak memeriksa keberhasilan operasi file, program dapat berjalan seolah-olah semuanya baik-baik saja, padahal proses pembacaan atau penulisan sebenarnya gagal.
Untuk itu, kita perlu memeriksa status stream.
Menggunakan is_open()
Method is_open() digunakan untuk memeriksa apakah file berhasil dibuka.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream file("data_murid.txt");
if (file.is_open()) {
cout << "File berhasil dibuka.";
file.close();
} else {
cout << "File gagal dibuka.";
}
return 0;
}
Jika file tersedia dan dapat dibuka, program akan menampilkan pesan bahwa file berhasil dibuka. Jika tidak, program menampilkan pesan kegagalan.
Menggunakan good(), fail(), dan bad()
Selain is_open(), stream menyediakan beberapa fungsi untuk memeriksa kondisi operasi.
| Method | Keterangan |
|---|---|
good() |
Mengembalikan true jika tidak ada flag kesalahan stream yang aktif. |
fail() |
Mengembalikan true jika operasi gagal, misalnya format data tidak sesuai atau file tidak berhasil dibuka. |
bad() |
Mengembalikan true jika terjadi kesalahan serius pada operasi stream. |
eof() |
Mengembalikan true jika kondisi akhir file telah terdeteksi. |
Contoh penggunaannya:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream file("data.txt");
if (file.fail()) {
cout << "Terjadi kegagalan membuka file.";
} else {
cout << "File berhasil dibuka.";
}
file.close();
return 0;
}
Perlu diketahui bahwa is_open() dan fail() tidak memeriksa hal yang persis sama. is_open() memeriksa apakah stream terhubung dengan file yang terbuka, sedangkan fail() memeriksa apakah flag kegagalan stream aktif.
8. Mengenal Error Handling
Sekarang kita beralih ke bagian kedua dari materi ini, yaitu penanganan kesalahan.
Ketika membuat program, kita tidak dapat mengasumsikan bahwa seluruh proses akan selalu berjalan sesuai rencana. Pengguna dapat memasukkan data yang salah, file dapat tidak ditemukan, atau program dapat menerima kondisi yang tidak dapat diproses.
Kesalahan seperti itu perlu ditangani agar program dapat memberikan respons yang tepat.
Proses mendeteksi dan menangani kesalahan disebut error handling.
Mengapa error handling diperlukan?
Bayangkan sebuah program meminta pengguna memasukkan angka untuk membagi suatu nilai.
Jika pengguna memasukkan angka nol sebagai pembagi, operasi pembagian tersebut tidak dapat dilakukan dalam aritmetika bilangan real biasa. Program perlu memeriksa kondisi tersebut sebelum melakukan perhitungan.
Contohnya:
#include <iostream>
using namespace std;
int main() {
double a, b;
cout << "Masukkan angka pertama: ";
cin >> a;
cout << "Masukkan angka kedua: ";
cin >> b;
if (b == 0) {
cout << "Kesalahan: pembagi tidak boleh nol.";
} else {
cout << "Hasil: " << a / b;
}
return 0;
}
Program tersebut menggunakan percabangan untuk memeriksa kondisi yang tidak diperbolehkan. Jika pembagi bernilai nol, program menampilkan pesan kesalahan dan tidak melakukan pembagian.
Ini merupakan salah satu bentuk penanganan kondisi yang tidak valid.
Apakah semua kesalahan harus menggunakan try-catch?
Tidak. Dalam C++, kita dapat menangani berbagai kondisi menggunakan percabangan, pemeriksaan nilai, status stream, maupun mekanisme exception.
Misalnya, jika kita hanya ingin memeriksa apakah suatu angka lebih besar dari nol, penggunaan if sudah cukup. Namun, untuk kondisi tertentu yang ingin dilaporkan sebagai exception, kita dapat menggunakan mekanisme try, throw, dan catch.
9. Menggunakan try, throw, dan catch
C++ menyediakan tiga keyword penting untuk mekanisme exception handling, yaitu try, throw, dan catch.
Ketiganya bekerja bersama untuk menangani kondisi tertentu yang dianggap sebagai exception.
try: bagian kode yang diawasi
Keyword try digunakan untuk membungkus bagian kode yang berpotensi menghasilkan exception.
throw: melempar exception
Keyword throw digunakan untuk melempar atau mengirimkan exception ketika suatu kondisi tertentu terjadi.
catch: menangkap exception
Keyword catch digunakan untuk menangkap exception yang dilempar dari blok try, kemudian menjalankan penanganan yang sesuai.
Contoh penggunaan try, throw, dan catch
Mari kita kembali ke contoh pembagian. Kali ini, kita akan menggunakan exception untuk menangani pembagian dengan nol.
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
double a, b;
cout << "Masukkan angka pertama: ";
cin >> a;
cout << "Masukkan angka kedua: ";
cin >> b;
try {
if (b == 0) {
throw runtime_error(
"Pembagi tidak boleh nol."
);
}
cout << "Hasil: " << a / b;
}
catch (const runtime_error& e) {
cout << "Kesalahan: " << e.what();
}
return 0;
}
Jika pengguna memasukkan angka kedua bernilai nol, program akan menghasilkan pesan:
Masukkan angka pertama: 10
Masukkan angka kedua: 0
Kesalahan: Pembagi tidak boleh nol.
Mari kita uraikan alurnya.
try mulai dijalankan.
b sama dengan nol, program menjalankan throw.
throw runtime_error(...) menghentikan alur normal pada blok try dan mencari handler yang sesuai.
runtime_error ditangkap oleh catch, kemudian pesan kesalahan ditampilkan menggunakan e.what().
Dengan mekanisme tersebut, kita dapat memisahkan kode utama dengan bagian yang bertugas menangani exception.
Mengapa menggunakan const runtime_error& e?
Perhatikan bagian berikut:
catch (const runtime_error& e) {
cout << e.what();
}
Parameter e merupakan referensi konstan terhadap exception yang ditangkap. Dengan cara ini, kita dapat membaca informasi exception tanpa membuat salinan objek dan tanpa mengubahnya.
Method what() digunakan untuk mendapatkan pesan penjelasan dari exception.
Pendalaman: apa itu std::exception?
C++ menyediakan class dasar bernama std::exception untuk berbagai jenis exception standar.
Beberapa class exception standar diturunkan dari class tersebut, seperti std::runtime_error dan std::logic_error.
Kita juga dapat menangkap berbagai jenis exception standar menggunakan:
catch (const exception& e) {
cout << e.what();
}
Pastikan menggunakan #include <exception> atau header terkait yang mendeklarasikan jenis exception yang digunakan. Untuk contoh runtime_error, gunakan #include <stdexcept>.
Menangani lebih dari satu jenis exception
Sebuah program dapat memiliki lebih dari satu blok catch. Setiap blok dapat menangani jenis exception yang berbeda.
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
int pilihan = 2;
try {
if (pilihan == 1) {
throw invalid_argument(
"Pilihan tidak valid."
);
}
if (pilihan == 2) {
throw runtime_error(
"Terjadi kesalahan proses."
);
}
}
catch (const invalid_argument& e) {
cout << "Input: " << e.what();
}
catch (const runtime_error& e) {
cout << "Proses: " << e.what();
}
return 0;
}
Jika nilai pilihan adalah 2, program akan menjalankan blok catch yang menangani runtime_error.
Blok catch diperiksa berdasarkan urutan penulisannya. Karena itu, ketika menangani class exception yang memiliki hubungan pewarisan, handler yang lebih spesifik sebaiknya diletakkan sebelum handler yang lebih umum.
10. Memahami Jenis Kesalahan dalam Program
Tidak semua kesalahan dalam program memiliki penyebab dan cara penanganan yang sama. Secara umum, kita dapat mengenali beberapa jenis kesalahan berikut.
1. Syntax error
Syntax error terjadi ketika penulisan kode tidak mengikuti aturan sintaks bahasa pemrograman.
Contohnya, lupa menambahkan tanda titik koma:
int nilai = 90
cout << nilai;
Pada contoh tersebut, deklarasi variabel tidak diakhiri dengan titik koma. Compiler akan melaporkan kesalahan ketika mencoba menerjemahkan program.
2. Runtime error
Runtime error merupakan kesalahan yang terjadi ketika program sedang dijalankan.
Contohnya, program mencoba membaca file yang tidak tersedia, melakukan operasi yang tidak valid, atau mengakses sumber daya yang gagal diperoleh.
Beberapa runtime error dapat ditangani melalui pemeriksaan kondisi, sedangkan kondisi tertentu dapat dilaporkan menggunakan exception.
3. Logical error
Logical error terjadi ketika program berhasil dikompilasi dan dijalankan, tetapi menghasilkan keluaran yang tidak sesuai dengan tujuan karena kesalahan logika.
Misalnya, kita ingin menghitung rata-rata tiga nilai, tetapi justru membaginya dengan dua.
int nilai1 = 80;
int nilai2 = 90;
int nilai3 = 100;
double rataRata =
(nilai1 + nilai2 + nilai3) / 2.0;
Kode tersebut dapat dikompilasi dan dijalankan. Namun, hasilnya salah karena jumlah nilai dibagi dua, bukan tiga.
| Jenis kesalahan | Terjadi kapan? | Contoh |
|---|---|---|
| Syntax error | Saat proses kompilasi. | Lupa titik koma atau salah menulis sintaks. |
| Runtime error | Saat program berjalan. | File gagal dibuka atau operasi tidak valid. |
| Logical error | Saat program menghasilkan hasil yang salah. | Rumus perhitungan tidak tepat. |
Ketiga jenis kesalahan tersebut membutuhkan pendekatan penanganan yang berbeda. try-catch bukan solusi untuk semua jenis kesalahan. Syntax error harus diperbaiki pada kode sumber, sedangkan logical error membutuhkan pemeriksaan logika program.
Ketika program tidak berjalan sesuai harapan, kita perlu menemukan sumber masalahnya. Proses tersebut dikenal sebagai debugging.
11. Membuat Program Pengelolaan Data dengan File
Sekarang kita akan menggabungkan konsep file handling dan pemeriksaan kesalahan dalam satu program sederhana.
Kita akan membuat program yang dapat menyimpan dan membaca catatan nama murid. Program menyediakan dua pilihan utama:
(- ) Menambahkan nama murid ke dalam file.
(- ) Menampilkan seluruh nama murid yang telah tersimpan.
Data akan disimpan dalam file bernama data_murid.txt. Setiap nama ditulis pada baris yang berbeda.
#include <iostream>
#include <fstream>
#include <string>
#include <limits>
using namespace std;
void tambahMurid() {
string nama;
cout << "Masukkan nama murid: ";
getline(cin, nama);
if (nama.empty()) {
cout << "Nama tidak boleh kosong.\n";
return;
}
ofstream file(
"data_murid.txt",
ios::app
);
if (!file.is_open()) {
cout << "Gagal membuka file.\n";
return;
}
file << nama << endl;
if (!file) {
cout << "Gagal menulis data.\n";
return;
}
file.close();
cout << "Data berhasil disimpan.\n";
}
void tampilkanMurid() {
ifstream file("data_murid.txt");
if (!file.is_open()) {
cout << "File belum tersedia atau gagal dibuka.\n";
return;
}
string nama;
int nomor = 1;
cout << "\nDaftar nama murid:\n";
while (getline(file, nama)) {
cout << nomor << ". " << nama << endl;
nomor++;
}
if (file.bad()) {
cout << "Terjadi kesalahan saat membaca file.\n";
}
file.close();
if (nomor == 1) {
cout << "Belum ada data murid.\n";
}
}
int main() {
int pilihan;
do {
cout << "\n=== DATA MURID ===\n";
cout << "1. Tambah murid\n";
cout << "2. Tampilkan murid\n";
cout << "3. Keluar\n";
cout << "Pilihan: ";
if (!(cin >> pilihan)) {
cout << "Input harus berupa angka.\n";
cin.clear();
cin.ignore(
numeric_limits<streamsize>::max(),
'\n'
);
continue;
}
cin.ignore(
numeric_limits<streamsize>::max(),
'\n'
);
switch (pilihan) {
case 1:
tambahMurid();
break;
case 2:
tampilkanMurid();
break;
case 3:
cout << "Program selesai.\n";
break;
default:
cout << "Pilihan tidak tersedia.\n";
}
} while (pilihan != 3);
return 0;
}
Memahami cara kerja program
Program tersebut menggunakan beberapa fungsi untuk memisahkan tugas-tugasnya.
tambahMurid()
Menerima nama dari pengguna, memeriksa apakah nama kosong, kemudian menyimpannya ke file menggunakan ofstream dan ios::app.
tampilkanMurid()
Membuka file menggunakan ifstream, membaca nama satu per satu, kemudian menampilkannya di layar.
main()
Menampilkan menu dan menerima pilihan pengguna. Perulangan do-while membuat menu tetap tersedia sampai pengguna memilih keluar.
Mengapa menggunakan numeric_limits?
Perhatikan bagian:
cin.ignore(
numeric_limits<streamsize>::max(),
'\n'
);
Ketika kita menggunakan cin >> pilihan, karakter newline yang dihasilkan ketika pengguna menekan Enter dapat tertinggal di dalam buffer input.
Jika setelah itu kita langsung menggunakan getline(), fungsi tersebut dapat membaca newline yang tertinggal dan menghasilkan string kosong.
Karena itu, kita menggunakan cin.ignore() untuk membuang sisa karakter hingga newline.
Selain itu, ketika input bukan angka, stream cin dapat masuk ke kondisi gagal. Kita menggunakan cin.clear() untuk menghapus status gagal dan cin.ignore() untuk membuang input yang tidak valid.
Mencoba program secara bertahap
Untuk memahami hasilnya, cobalah menjalankan program dengan urutan berikut:
(- ) Jalankan program untuk pertama kali.
(- ) Pilih menu tambah murid dan masukkan nama Andi.
(- ) Tambahkan nama Budi dan Citra.
(- ) Pilih menu tampilkan murid.
(- ) Tutup program, kemudian jalankan kembali.
(- ) Pilih menu tampilkan murid sekali lagi.
Jika seluruh proses berhasil, nama-nama yang sudah dimasukkan sebelumnya tetap dapat ditampilkan meskipun program telah ditutup dan dijalankan kembali.
12. Hal yang Perlu Diperhatikan Saat Mengolah File
Dalam program nyata, pengolahan file memerlukan perhatian lebih dari sekadar membuka, membaca, dan menulis. Ada beberapa kebiasaan yang sebaiknya diterapkan.
1. Periksa keberhasilan membuka file
Jangan langsung membaca atau menulis file tanpa memastikan bahwa file berhasil dibuka.
2. Tutup file setelah selesai digunakan
Gunakan close() ketika operasi telah selesai. Selain itu, stream file C++ juga akan ditutup secara otomatis ketika objek stream keluar dari masa hidupnya.
3. Gunakan mode pembukaan yang sesuai
Jika ingin mengganti isi file, gunakan mode output yang sesuai. Jika ingin menambahkan catatan, gunakan ios::app.
4. Periksa data yang dibaca
Jangan menganggap seluruh isi file selalu sesuai dengan format yang diharapkan. File dapat kosong, berubah, atau berisi data yang tidak sesuai.
5. Hindari mengabaikan pesan kesalahan
Ketika terjadi kegagalan, tampilkan informasi yang cukup jelas agar pengguna mengetahui apa yang terjadi dan apa yang dapat dilakukan selanjutnya.
Pendalaman: apakah file harus selalu berupa file teks?
Tidak. C++ juga dapat mengolah file biner menggunakan mode ios::binary.
File teks menyimpan informasi dalam bentuk karakter yang relatif mudah dibaca manusia, misalnya .txt atau .csv.
File biner menyimpan data dalam bentuk representasi byte. Format ini dapat digunakan untuk menyimpan data yang tidak dimaksudkan untuk dibaca langsung sebagai teks.
Pada tahap awal, file teks lebih mudah dipahami dan cukup untuk latihan file handling.
13. Latihan Pemahaman
Setelah mempelajari file handling dan error handling, cobalah mengerjakan latihan berikut.
Jelaskan perbedaan antara menyimpan data dalam variabel dan menyimpan data dalam file.
(- ) Mengapa data dalam variabel tidak otomatis tersedia setelah program ditutup?
(- ) Apa manfaat menyimpan data dalam file?
Jelaskan perbedaan fungsi dari:
(- ) ifstream
(- ) ofstream
(- ) fstream
Berikan masing-masing satu contoh penggunaannya.
Apa perbedaan antara ios::out, ios::app, dan ios::binary?
Mengapa kita perlu memilih mode pembukaan file yang sesuai dengan kebutuhan program?
Jelaskan fungsi dari keyword berikut:
(- ) try
(- ) throw
(- ) catch
Kemudian jelaskan apa yang terjadi ketika sebuah exception dilempar di dalam blok try.
Tentukan apakah kondisi berikut termasuk syntax error, runtime error, atau logical error.
(- ) Program lupa menambahkan titik koma.
(- ) Program menghasilkan rata-rata yang salah karena rumusnya keliru.
(- ) Program gagal membuka file yang ingin dibaca.
Buatlah program C++ yang dapat menyimpan dan membaca catatan kegiatan harian.
Ketentuan program:
(- ) Memiliki menu untuk menambahkan catatan.
(- ) Memiliki menu untuk menampilkan seluruh catatan.
(- ) Setiap catatan disimpan pada baris baru dalam file.
(- ) Data lama tidak boleh terhapus ketika catatan baru ditambahkan.
(- ) Program harus memeriksa apakah file berhasil dibuka.
(- ) Program dapat kembali ke menu utama setelah satu operasi selesai.
Lihat petunjuk pengerjaan
Gunakan beberapa komponen berikut:
(- ) ofstream untuk menyimpan catatan.
(- ) ios::app untuk menambahkan catatan.
(- ) ifstream untuk membaca catatan.
(- ) while (getline(file, catatan)) untuk membaca setiap baris.
(- ) if untuk memeriksa keberhasilan membuka file.
14. Kesimpulan
File handling dan error handling merupakan dua kemampuan penting ketika kita mulai membuat program yang lebih realistis.
File handling memungkinkan program menyimpan dan membaca data dari media penyimpanan sehingga informasi dapat digunakan kembali meskipun program sudah ditutup.
Sementara itu, error handling membantu program mengenali kondisi yang tidak berjalan sesuai harapan dan memberikan respons yang tepat.
Beberapa hal penting yang telah dipelajari:
(- ) File digunakan untuk menyimpan data agar dapat digunakan kembali.
(- ) fstream merupakan library C++ untuk mengolah file.
(- ) ifstream digunakan untuk membaca file.
(- ) ofstream digunakan untuk menulis file.
(- ) fstream dapat digunakan untuk membaca dan menulis file.
(- ) ios::app digunakan untuk menambahkan data di akhir file.
(- ) is_open() dan status stream dapat digunakan untuk memeriksa keberhasilan operasi file.
(- ) Error handling membantu program merespons kondisi yang tidak diharapkan.
(- ) try, throw, dan catch merupakan bagian dari mekanisme exception handling.
(- ) Syntax error, runtime error, dan logical error merupakan jenis kesalahan yang perlu dikenali ketika mengembangkan program.
Dengan memahami materi ini, kita sudah dapat membuat program yang tidak hanya menerima dan mengolah data, tetapi juga menyimpan informasi serta memeriksa kondisi ketika proses tidak berjalan sesuai rencana.
Selanjutnya: Mengembangkan Program dengan Struktur yang Lebih Baik
Setelah memahami fungsi, array, pointer, OOP, file handling, dan error handling, kita sudah memiliki banyak komponen dasar untuk membangun aplikasi C++.
Tahap berikutnya dapat diarahkan pada pengembangan program yang lebih terstruktur, seperti memisahkan kode ke beberapa file, menggunakan header file, memahami namespace, dan mengenal cara kerja library.
Lanjutkan ke materi Mengorganisasi Program C++ dengan Header dan Source File →
Jika ingin mengulang konsep yang berkaitan dengan materi ini, silakan kunjungi:
(- ) Memahami Struktur Dasar Program C++
(- ) Belajar Variabel, Tipe Data, dan Konstanta
(- ) Menerima Input dan Mengolah Data
(- ) Membuat Program yang Dapat Mengambil Keputusan
(- ) Mengulang Instruksi dengan Perulangan
(- ) Menyusun Program dengan Fungsi