Bab. 9 Pemrosesan File
Tujuan
Instruksional
|
|
·
File dan aliran.
·
Menciptakan file
sekuensial.
·
Membaca data dari
file sekuensial.
·
File akses-acak.
·
Menciptkan file
akses-acak.
|
·
Menulis secara acak
ke dalam file akses-acak.
·
Membaca file
akses-acak secara sekuensial.
|
9.1 Introduksi
Penyimpanan data di dalam memori
bersifat sementara. File dipakai untuk menyimpan data secara permanen. Komputer
menyimpan file pada divais penyimpanan sekunder, seperti hard disk, CD, DVD,
flash disk, dan lainnya. Pada bab ini, akan dijelaskan bagaimana membangun
program C++ yang menciptakan, memperbarui, dan memproses data file. Akan
dipelajari tentang file sekuensial dan fila akses-acak. Akan dibandingkan
pemrosesan file data-terformat dengan pemrosesan file data-mentah.
9.2 File dan Aliran
C++
memandang bahwa setiap file hanyalah sebuah runtun byte (Gambar 9.1). Setiap
file diakhiri dengan penanda end-of-file atau sejumlah byte tertentu yang
direkam di dalam suatu struktur data sistem operasi. Ketika sebuah file dibuka,
suatu objek diciptakan, dan aliran diasosiasikan dengan objek tersebut. Anda
telah melihat bahwa objek cin, cout, cerr, dan clog diciptakan
ketika <iostream> dicantumkan.
Aliran yang diasosiasikan dengan objek menyediakan kanal komunikasi antara
program dan file atau divais tertentu. Sebagai contoh, objek cin (objek aliran masukan standard)
memampukan program untuk memasukkan data dari papanketik atau dari divais
lainnya, objek cout (objek aliran
keluaran standard) memampukan program untuk mengeluarkan data ke layar atau
divais lainnya, dan objek cerr dan clog (objek aliran error standard)
memampukan program untuk mengeluarkan error ke layar atau ke divais lainnya.
Gambar
9.1 C++ memandang file sebagai sebuah runtun byte
Untuk
melakukan pemrosesan file di dalam C++, header <iostream> dan <fstream>
harus dicantumkan. Header <fstream>
menyertakan definisi untuk template kelas aliran basic_ifstream (untuk masukan file), basic_ofstream (untuk keluaran file), dan basic_fstream (untuk keluaran dan masukan file). Setiap template
kelas memiliki spesialisasi template terdefinisi yang memampukan I/O char.
Selain
itu, pustaka <fstream>
menyediakan sejumlah nama alias typedef
untuk spesialisasi template ini. Sebagai contoh, typedef ifstream merepresentasikan sebuah spesialisasi dari basic_ifstream yang memampukan masukan char dari sebuah file. Sama halnya, typedef ofstream merepresentasikan
spesialisasi atas basic_ofstream yang
memampukan keluaran char ke file. Begitu
juga dengan typedef fstream
merepresentasikan spesialisasi atas basic_fstream
yang memampukan masukan char dari dan
keluaran char ke file.
File
dibuka dengan cara menciptakan objek dari spesialisasi template aliran
tersebut. Template tersebut diderivasi dari template kelas basic_istream, basic_ostream,
dan basic_iostream. Jadi, semua
fungsi anggota, operator, dan manipulator yang dimiliki oleh template tersebut
dapat juga diterapkan pada aliran file. Gambar 9.2 menyimpulkan relasi
pewarisan atas kelas I/O yang telah didiskusikan sejauh ini.
Gambar
9.2 Sebagian dari hirarki pewarisan template kelas I/O
16.3 Menciptakan File Sekuensial
C++
tidak memakai struktur apapun pada sebuah file. Jadi, Anda harus membuat file
menjadi terstruktur sesuai dengan kebutuhan aplikasi Anda. Contoh berikut
menunjukkan bagaimana Anda dapat melakukan
penstrukturan sederhana pada suatu file.
Gambar
16.3 menciptakan sebuah file sekuensial yang bisa dipakai untuk sistem akun
sederhana di dalam suatu bank. Untuk setiap klien, program mendapatkan nomor
akun, nama, dan saldo klien. Data yang didapatkan untuk tiap klien membentuk
rekaman bagi klien tersebut. Nomor akun berperan sebagai kunci rekaman; yaitu,
program menciptakan rekaman file dengan urutan nomor akun. Program ini mengasumsikan
bahwa pengguna memasukkan rekaman sesuai dengan urutan nomor akun. Dalam sistem
akun yang lebih komprehensif, kapabilitas pengurutan tentu diperlukan.
Gambar 9.1 Menggunakan Spesialisasi Template Fungsi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
// Gambar 9.3: gambar9_03.cpp
// Menciptakan suatu file sekuensial.
#include <iostream>
#include <string>
#include <fstream> // aliran file
#include <cstdlib>
using namespace std;
int main()
{
//
konstruktor ofstream membuka file
ofstream keluarFileKlien(
"klien.txt", ios::out );
// keluar program jika tidak bisa
menciptakan file
if( !keluarFileKlien )// operator ! dioverload
{
cerr << "File tidak bisa
dibuka" << endl;
exit( 1 );
} // akhir dari if
cout << "Masukkan akun, nama,
dan saldo." << endl
<< "Masukkan end-of-file
untuk mengakhiri masukan.\n? ";
int akun;
string nama;
double saldo;
// membaca akun,
nama, dan saldo dari cin kemudian menempatkannya di dalam file
while( cin >> akun >> nama >> saldo )
{
keluarFileKlien << akun
<< ' ' << nama << ' ' << saldo << endl;
cout << "? ";
} // akhir dari while
} // akhir dari main
|
Masukkan akun, nama, dan saldo.
Masukkan end-of-file untuk mengakhiri masukan.
? 100 Rico 32.90
? 200 Robert 342.37
? 300 Ika 0.00
? 400 Rotua -43.32
? 500 Teti 323.45
?^Z
Seperti
dinyatakan sebelumnya, file dibuka dengan menciptakan objek ifstream, ofstream, dan fstream.
Pada Gambar 9.3, file dibuka untuk keluaran, sehingga objek ofstream yang diciptakan. Dua argumen
dilewatkan kepada konstruktor objek, nama file dan mode file-open (baris 12).
Untuk suatu objek ofstream, mode file-open dapat berupa ios::out untuk mengeluarkan data ke sebuah file atau berupa ios::app untuk menyambungkan data ke
akhir suatu file (tanpa memodifikasi data yang sebelumnya sudah ada di dalam
file itu). Data di dalam file yang sudah ada bila dibuka dengan mode ios::out akan dihapus. Jika file yang
dispesifikasi belum ada, maka objek ofstream
akan menciptakan file tersebut dengan nama yang diberikan.
Baris
12 mencipatakan sebuah objek ofstream
bernama keluarFileKlien yang
diasosiasikan dengan file klien.txt,
yang dibukan untuk keluaran. Argumen “klien.txt”
dan ios::out dilewatkan kepada
konstruktor ofstream, yang membukan
file untuk menyediakan kanal komunikasi dengan file itu. Secara default, objek ofstream dibuka untuk keluaran, jadi
baris 12 dapat menggunakan statemen alternatif
ofstream
keluarFileKlien( "clients.txt"
);
untuk
membuka file klien.txt untuk
keluaran. Gambar 9.4 membuat daftar beberapa mode file-open. Semua mode itu
dapat dimodifikasikan, seperti yang akan didiskusikan nanti.
Gambar
9.4 Beberapa Mode Pembukaan File
Mode
|
Penjelasan
|
ios::app
ios::ate
ios::in
ios::out
ios::trunc
ios::binary
|
Menyambung keluaran ke
akhir file.
Membuka file untuk
keluaran dan bergerak ke akhir file (biasanya untuk menyambung data ke dalam
sebuah file). Data dapat ditulis di sembarang tempat di dalam file.
Membuka file untuk
masukan.
Membuka file untuk
keluaran.
Membuang isi file (ini
juga aksi default untuk ios::out).
Membuka file untuk
masukan dan keluaran biner (non-teks).
|
Sebuah
objek ofstream dapat diciptakan tanpa
membuka file tertentu, karena file dapat diasosiasikan ke objek itu belakangan.
Sebagai contoh, statemen
ofstream
keluarFileKlien;
menciptakan
sebuah objek ofstream bernama keluarFileKlien. Fungsi anggota open dari kelas ofstream membuka file dan mengasosiasikannya ke objek ofstream yang sudah ada sebagai berikut:
keluarFileKlien.open( "clients.txt", ios::out
);
Setelah
menciptakan sebuah objek ofstream dan
mencoba membukanya, program kemudian menguji apakah operasi pembukaan berhasil
atau tidak. Statemen if pada baris
15-19 menggunakan fungsi anggota operator ! teroverload untuk menentukan apakah operasi pembukaan berhasil atau
tidak. Kondisi ini menghasilkan nilai true
jika salah satu failbit atau badbit ditetapkan true untuk aliran pada operasi open.
Beberapa kejadian error di antaranya adalah mencoba membuka file yang tidak ada
untuk pembacaan, mencoba membuka file atau menulis file dari sebuah direktori
dimana Anda tidak memiliki ijin akses,dan membuka file untuk menulis ketika
memori tidak lagi tersedia.
Jika
kondisi mengindikasikan terjadinya percobaan yang tidak berhasil dalam membuka
sebuah file, maka baris 17 menampilkan pesan error “File tidak bisa dibuka”, dan baris 18 memanggil fungsi exit untuk menghentikan program. Argumen
0 pada fungsi exit mengindikasikan
bahwa program berhenti secara normal; Argumen bernilai selain 0 mengindikasikan
bahwa program berhenti karena terjadinya suatu error.
Fungsi
anggota ios lainnya, operator void *, mengkonversi aliran
menjadi sebuah pointer, sehingga dapat diuji sebagai 0 (pointer null) atau
tak-null (sembarang nilai pointer). Ketika sebuah nilai pointer digunakan
sebagai kondisi, C++ menginterpretasikan pointer null sebagai nilai false dan menginterpretasikan pointer
tak-null sebagai true. Jika failbit atau badbit ditetapkan (true)
untuk aliran, maka dihasilkan nilai balik 0 (false). Kondisi di dalam statemen while pada baris 29-33 memanggil
fungsi anggota operator void * pada cin secara implisit. Kondisi tetap
bernilai true sepanjang tidak ada failbit atau badbit yang bernilai true
untuk cin.
Jika
baris 12 membuka file secara sukses, program akan mulai memproses data. Baris
21-22 mendesak pengguna untuk memasukkan nilai untuk tiga bidang pada tiap
rekaman atau memasukkan indikator end-of-file tidak pengentrian data selesai
dilakukan. Gambar 9.5 mencantumkan kombinasi kunci untuk indikator end-of-file
pada berbagai sistem operasi.
Gambar
9.5 Kombinasi Kunci untuk Indikator End-of-File
Sistem
Operasi
|
Kombinasi
Kunci
|
UNIX/LINUX/Mac OS X
Microsoft Windows
|
<Ctrl-d>
<Ctrl-z> (
kadang-kadang diikuti dengan penekanan ENTER)
|
Baris
29 mengekstrak setiap himpunan data dan menentukan apakah end-of-file telah dimasukkan oleh pengguna atau tidak. Ketika
end-of-file dijumpai atau bila data buruk dimasukkan, operator void * menghasilkan pointer null (yang dikonversi menjadi
nilai false) dan statemen while berhenti. Pengguna mengentrikan
end-of-file untuk menginformasikan program bahwa pengentrian data telah
selesai. Indikator end-of-file ditetapkan ketika pengguna menekan kombinasi
kunci tertentu, yang dicantumkan pada Gambar 9.5. Statemen while tetap beriterasi sampai indikator end-of-file dientrikan oleh
pengguna.
Baris
31 menuliskan sehimpunan data ke dalam file klien.txt,
menggunakan operator penyisipan aliran << dan objek keluarFileKlien yang diasosiasikan denagan file tersebut di awal
program. Data yang diciptakan pada Gambar 16.3 berupa file teks sederhana, jadi
dapat dibaca oleh sembarang text editor.
Begitu
pengguna mengentrikan indikator end-of-file,
maka fungsi main berhenti. Ini secara
implisit memanggil destruktor keluarFileKlien,
yang menutup file klien.txt. Anda
dapat pula menutup objek ofstream
secara eksplisit, menggunakan fungsi anggota close di dalam statemen
keluarFileKlien.close();
Pada
contoh eksekusi program, pengguna mengentrikan informasi untuk lima akun,
kemudian memasukkan indikator end-of-file untuk mengindikasikan bahwa
pengentrian data telah selesai. Untuk memverifikasi bahwa program telah
menciptakan file dengan sukses, pada bagian berikutnya akan ditunjukkan
bagaimana menciptakan sebuah program untuk membaca file ini dan menampilkan
isinya.
9.3 Membaca Data dari File Sekuensial
File
menyimpan data agar dapat dibaca dan diproses ketika dibutuhkan. Pada bagian
sebelumnya telah didemonstrasikan bagaimana menciptakan sebuah file sekuensial.
Sekarang akan didiskusikan bagaimana membaca data secara sekuensial dari suatu
file. Gambar 9.6 membaca dan menampilkan lima rekaman dari file klien.txt, yang
telah diciptakan menggunakan program pada Gambar 9.3. Penciptaan sebuah objek ifstream dimaksudkan untuk membuka file
untuk masukan. Konstruktor ifstream
dapat menerima nama file dan mode file-open sebagai argumen. Baris 15
menciptakan suatu objek ifstream yang
dinamakan masukanFileKlien dan
mengasosiasikannya dengan file klien.txt.
Argumen di dalam kurung dilewatkan kepada konstruktor ifstream, yang membuka file dan menyediakan kanal komunikasi dengan
file tersebut.
Gambar 9.6 Membaca dan Menampilkan Isi Suatu File
Sekuensial
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// Gambar 9.6: gambar9_06.cpp
// Membaca dan menampilkan isi suatu file
sekuensial.
#include <iostream>
#include <fstream> // aliran file
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
void keluaranBaris( int, const string, double
); // prototipe
int main()
{
// konstruktor
ifstream untuk membuka file
ifstream masukanFileKlien(
"klien.txt", ios::in );
//
keluar program jika ifstream tidak bisa membukan file
if(
!masukanFileKlien
)
{
cerr << "File tidak bisa
dibuka" << endl;
exit( 1 );
} // akhir dari if
int akun;
string nama;
double saldo;
cout << left << setw( 10 )
<< "Akun" << setw( 13 )
<< "Nama" <<
"Saldo" << endl << fixed << showpoint;
//
menampilkan setiap rekaman di dalam file
masukanFileKlien >> akun
>> nama >> saldo
keluaranBaris( akun, nama, saldo );
} // akhir dari main
// menampilkan satu rekaman dari file
void keluaranBaris( int akun, const
string nama, double saldo )
{
cout << left << setw( 10 )
<< akun << setw( 13 ) << nama
<< setw( 7 ) << setprecision(
2 ) << right << saldo << endl;
} // akhir dari fungsi keluaranBaris
|
Akun Nama Saldo
100 Rico 32.90
200 Robert 342.37
300 Ika
0.00
400 Rotua -43.32
500 Teti 323.45
Objek
dari kelas ifstream dibuka untuk
masukan secara default, sehingga statemen
ifstream masukanFileKlien(
"klien.txt"
);
membuka
klien.txt untuk masukan. Sama seperti objek ofstream,
objek ifstream dapat diciptakan tanpa
perlu membuka fil tertentu, karena sebuah file dapat diasosiasikan dengannya
belakangan.
Sebelum
mencoba membaca data dari file, program menggunakan kondisi !masukanFileKlien untuk menentukan
apakah file dibuka secara sukes atau tidak. Baris 32 membaca sehimpunan data
(rekaman) dari file. Setelah baris 32 dieksekusi pertama kali, akun memiliki nilai 100, nama memiliki nilai “Rico”, dan saldo memiliki nilai 32.90. Setiap kali baris 32 dieksekusi, ia
membaca rekaman lain dari file dan ditugaskan kepada variabel akun, nama, dan saldo. Baris 33
menampilkan rekaman, menggunakan fungsi keluaranBaris
(baris 37-41), yang menggunakan manipulator aliran terparameterisasi untuk
memformat data tampilan. Ketika end-of-file dijumpai, pemanggilan implisit
terhadap operator void * di dalam
kondisi while menghasilkan nilai
balik pointer null (yang dikonversi menjadi false),
destruktor ifstream menutup file, dan
program berhenti.
Untuk
membaca atau mengambil data secara sekuensial dari sebuah file, program
nirmalnya mulai membaca dari awal file dan membaca semua data secara berurutan
sampai semua data yang diinginkan terbaca. Beberapa fungsi anggota dari kelas istream dan ostream disediakan untuk memposisikan-ulang pointer di dalam file.
Fungsi anggota itu adalah seekg untuk
istream dan seekp untuk ostream.
Setiap objek istream memiliki sebuah
pointer get, yang mengindikasikan jumlah byte di dalam file dimana lokasi
masukan berikutnya dilakukan, dan setiap objek ostream memiliki pointer put, yang mengindikasikan jumlah byte di
dalam file diman lokasi keluaran berikutnya ditempatkan. Statemen
masukanFileKlien.seekg(
0 );
memposisikan-ulang
pointer di dalam file untuk menunjuk ke awal file (lokasi 0) yang diasosiasikan
dengan masukanFileKlien. Argumen dari
fungsi seekg adalah sebuah integer long. Argumen keduanya dapat
dispesifikasi untuk mengindikasikan arah pembacaan, yang dapat berupa ios::beg (default) untuk memposisikan
pointer ke awal aliran, ios::cur
untuk memposisikan pointer ke posisi sekarang di dalam aliran, atau ios::end untuk memposisikan pointer ke
akhir sebuah aliran. Pointer penunjuk di dalam file merupakan sebuah nilai
integer yang menspesifikasi lokasi di dalam file sebagai jumlah byte dari
lokasi awal file. Beberapa contoh pemosisian pointer get adalah
// memposisikan ke byte ke-n dari objekFile (diasumsikan
ios::beg)
objekFile.seekg( n );
// memposisikan n byte maju di dalam
objekFile
objekFile.seekg( n, ios::cur );
// memposisikan n byte mundur dari akhir
objekFile
objekFile.seekg( n, ios::end );
// memposisikan ke akhir objekFile
objekFile.seekg( 0, ios::end );
Operasi
yang sama dapat dilakukan menggunakan fungsi anggota seekp dari kelas ostream.
Fungsi anggota tellg dan tellp disediakan untuk menghasilkan
nilai balik berupa lokasi sekarang dari pointer put dan get. Statemen berikut
menugaskan nilai pointer get kepada variabel lokasi bertipe long:
lokasi =
objekFile.tellg();
Gambar
9.7 memampukan seorang manajer kredit untuk menampilkan informasi akun untuk
para kustomer dengan saldo nol (kustomer yang tidak meminjam uang berapapun
kepada bank), saldo kredit (negatif) (kustomer yang meminjamkan uang kepada
bank), dan saldo debit (positif) (kustomer yang meminjam uang dari bank).
Program menampilkan sebuah menu dan mengijinkan manajer kredit untuk memasukkan
salah satu dari tiga opsi untuk mendapatkan informasi kredit. Opsi 1
menghasilkan daftar akun dengan saldo nol. Opsi 2 menghasilkan daftar akun
dengan saldo kredit. Opsi 3 menghasilkan daftar akun dengan saldo debit. Opsi 4
menghentikan eksekusi program. Pengentrian opsi yang tidak valid akan
menampilkan desakan agar sang manajer untuk memasukkan pilihan lagi. Baris
65-66 memampukan program untuk membaca dari awal file setelah penanda EOF
dibaca.
Gambar 9.7 Program untuk Menampilkan Akun
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
// Gambar 9.7: gambar9_07.cpp
// Program untuk menampilkan akun.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <cstdlib>
using namespace std;
enum TipePermintaan { SALDO_NOL = 1, SALDO_KREDIT, SALDO_DEBIT, AKHIR };
int getPermintaan();
bool harusTampil( int, double );
void keluaranBaris( int, const string, double
);
int main()
{
//
konstruktor ifstream untuk membuka file
ifstream masukanFileKlien(
"klien.txt", ios::in );
//
keluar program jika ifstream tidak bisa membuka file
if(
!masukanFileKlien
)
{
cerr << "File tidak bisa dibuka" << endl;
exit( 1 );
} //
akhir dari if
int
permintaan;
int
akun;
string nama;
double
saldo;
//
mendapatkan permintaan pengguna (saldo nol, kredit, atau debit)
permintaan = getPermintaan();
// memproses permintaan pengguna
while ( permintaan != AKHIR )
{
switch ( permintaan )
{
case SALDO_NOL:
cout << "\nAkun dengan
saldo nol:\n";
break;
case SALDO_KREDIT:
cout << "\nAkun dengan
saldo kredit:\n";
break;
case SALDO_DEBIT:
cout << "\nAkun dengan
saldo debit:\n";
break;
} // akhir dari switch
// membaca akun, nama, dan saldo dari file
masukanFileKlien >> akun >> nama >> saldo;
//
menampilkan isi file (sampai eof)
while( !masukanFileKlien.eof() )
{
// menampilkan rekaman
if ( harusTampil( permintaan,
saldo ) )
keluaranBaris( akun, nama, saldo );
//
membaca akun, nama, dan saldo dari file
masukanFileKlien >> akun
>> nama >> saldo;
} // akhir dari while sebelah dalam
masukanFileKlien.clear(); // mereset eof untuk masukan berikutnya
masukanFileKlien.seekg( 0 ); // mereposisi ke awal file
permintaan = getPermintaan(); // mendapatkan permintaan tambahan dari pengguna
} // akhir dari while sebelah luar
cout
<< "Akhir dari eksekusi." << endl;
} // akhir dari main
// mendapatkan permintaan dari pengguna
int getPermintaan()
{
int permintaan; // permintaan dari
pengguna
// menampilkan pilihan permintaan
cout << "\nMasukkan pilihan"
<< endl
<< " 1 - Daftar akun dengan
saldo nol" << endl
<< " 2 - Daftar akun dengan
saldo kredit" << endl
<< " 3 - Daftar akun dengan
saldo debit" << endl
<< " 4 - Akhir dari eksekusi"
<< fixed << showpoint;
do // membaca permintaan pengguna
{
cout << "\n? ";
cin >> permintaan;
} while ( permintaan < SALDO_NOL
&& permintaan > AKHIR );
return permintaan;
} // akhir dari fungsi getPermintaan
// menentukan apakah menampilkan rekaman
yang diberikan
bool harusTampil( int tipe, double saldo
)
{
// menentukan apakah harus menampilkan saldo
nol
if ( tipe == SALDO_NOL &&
saldo == 0 )
return true;
// menentukan apakah harus menampilkan saldo
kredit
if ( tipe == SALDO_KREDIT &&
saldo < 0 )
return true;
// menentukan apakah harus menampilkan saldo
debit
if ( tipe == SALDO_DEBIT &&
saldo > 0 )
return true;
return false;
} // akhir dari fungsi harusTampil
// menampilkan satu rekaman dari file
void keluaranBaris( int akun, const
string nama, double saldo )
{
cout << left << setw( 10 )
<< akun << setw( 13 ) << nama
<< setw( 7 ) << setprecision(
2 ) << right << saldo << endl;
} // akhir dari fungsi keluaranBaris
|
Masukkan pilihan
1 - Daftar akun dengan saldo nol
2 - Daftar akun dengan saldo kredit
3 - Daftar akun dengan saldo debit
4 - Akhir dari eksekusi
? 1
Akun dengan saldo nol:
300 Ika 0.00
Masukkan pilihan
1 - Daftar akun dengan saldo nol
2 - Daftar akun dengan saldo kredit
3 - Daftar akun dengan saldo debit
4 - Akhir dari eksekusi
? 2
Akun dengan saldo kredit:
400 Rotua -43.32
Masukkan pilihan
1 - Daftar akun dengan saldo nol
2 - Daftar akun dengan saldo kredit
3 - Daftar akun dengan saldo debit
4 - Akhir dari eksekusi
? 3
Akun dengan saldo debit:
100 Rico 32.90
200 Robert 342.37
500 Teti 323.45
Masukkan pilihan
1 - Daftar akun dengan saldo nol
2 - Daftar akun dengan saldo kredit
3 - Daftar akun dengan saldo debit
4 - Akhir dari eksekusi
? 4
Akhir dari eksekusi.
9.4 File Akses-Acak
Sejauh
ini, Anda telah melihat bagaimana menciptakan file sekuensial. File sekuensial
cocok untuk aplikasi akses-instan, dimana di dalamnya rekaman tertentu harus
dicari lokasinya secara sangat cepat. Aplikasi akes-instan yang umum dipakai
adalah sistem reservasi penerbangan, sistem perbankan, dan sistem pemrosesan
transaksi lain yang memerlukan akses cepat terhadap data tertentu. Bank bisa
saja memiliki ratusan atau bahkan jutaan nasabah, yang sangat memerlukan sistem
akes instan di dalam pelayanannya. Akses instan ini diwujudkan dengan file
akes-acak. Rekaman individual atas sebuah file akses-acak dapat diakses secara
langsung (dan secara cepat) tanpa harus mencari atau meneliti rekaman lain.
Seperti
yang telah dikatakan, C++ tidak memaksakan struktur apapun pada sebuah file.
Jadi, aplikasi yang ingin menggunakan file akses-acak harus diciptakan sendiri
oleh pengguna. Berbagai teknik telah digunakan selama ini. Metode termudah
untuk mewujudkannya adalah bahwa semua rekaman di dalam file harus sama
panjang. Dengan rekaman panjang sama, program semakin mudah untuk menghitung
lokasi persis dari sembarang rekaman yang relatif terhadap awal file. Akan
ditunjukkan bagaimana hal ini memfasilitasi akses segera terhadap rekaman
tertentu, bahkan bila di dalam file besar.
Gambar
9.8 mengilustrasikan cara pandang C++ terhadap file akses-acak yang memuat
rekaman-rekaman dengan panjang sama (setiap rekaman, pada kasus ini, memiliki
panjang 100 byte). File akses-acak seperti jalan raya dimana banyak kendaraan
berukuran sama berjejer, beberapa kendaraan berpenumpang tetapi lainnya kosong.
Gambar
9.8 Cara pandang C++ atas file akses-acak
Data
dapat disisipkan ke dalam sebuah file akses-acak tanpa harus merusak data lain
di dalam file. Data yang disimpan sebelumnya juga dapat diperbarui atau diharus
tanpa perlu menulis-ulang keseluruhan file. Selanjutnya akan dijelaskan
bagaimana menciptakan sebuah file akses-acak, mengentrika data ke dalam file,
membaca data secara sekuensial dan secara acak, memperbarui data, dan menghapus
data yang tidak lagi dibutuhkan.
9.5 Menciptakan File Akses-Acak
Fungsi
anggota write pada kelas ostream mengeluarkan sejumlah tetap
byte, dimulai dari lokasi tertentu di dalam memori, ke aliran yang
dispesifikasi. Ketika aliran diasosiasikan dengan sebuah file, fungsi write menulis data pada lokasi di dalam
file yang dispesifikasi oleh pointer put. Fungsi anggota read pada kelas istream
memasukkan sejumlah tetap byte dari aliran yang dispesifikasi ke suatu area di
dalam memori dimulai dari alamat tertentu. Jika aliran diasosiasikan dengan
sebuah file, maka fungsi read
memasukkan byte-byte pada lokasi di dalam file terspesifikasi oleh pointer get.
Menuliskan
Byte-Byte dengan Fungsi Anggota write
Ketika
menuliskan integer angka ke dalam
sebuah file, daripada menggunakan statemen
keluarFile
<< angka;
dimana
untuk integer empat byte ditampilkan sedikit-dikitnya satu dijit dan
sebanyak-banyaknya 11 dijit (10 dijit ditambah tanda positif/negatif), lebih
baik Anda menggunakan statemen
keluarFile.write( reinterpret_cast<
const
char *
>( &angka ),
sizeof(
angka ) );
yang
selalu menuliskan versi biner atas integer empat byte, angka. Fungsi write
memperlakukan argumennya sebagai sekelompok byte dengan memandang objek di
dalam memori sebagai sebuah const char
* (pointer yang menunjuk ke sebuah byte). Mulai dari lokasi tersebut, fungsi write mengeluarkan sejumlah byte yang
dispesifikasi oleh argumen keduanya, yaitu sebuah integer bertipe size_t.
Seperti yang nanti Anda lihat, fungsi read
dari kelas istream dapat digunakan
untuk membaca secara berurutan empat byte ke arah variabel integer angka.
Mengkonversi
antara Tipe Pointer dengan Operator reinterpret_cast
Sayangnya,
kebanyakan pointer yang dilewatkan kepada fungsi write sebagai argumen pertama bukanlah bertipe const char *. Untuk mengeluarkan objek bertipe lain, Anda harus
mengkonversi pointer yang menunjuk ke objek itu menjadi tipe cons char *. Jika tidak, kompiler tidak
akan mengkompilasi pemanggilan terhadap fungsi write. C++ menyediakan operator reinterpret_cast
untuk kasus semacam ini. Tanpa reinterpret_cast,
statemen write yang mengeluarkan
integer angka tidak akan dikompilasi
karena kompiler tidak mengijinkan pointer bertipe int * (tipe yang dijadikan nilai balik oleh ekspresi &angka) dilewatkan kepada sebuah fungsi
yang mengharapkan argumen bertipe const
char *.
Operator
reinterpret_cast dilakukan pada saat
kompilasi dan tidak mengubah nilai objek yang ditunjuk oleh operandnya.
Operator ini meminta kompiler untuk menginterpretasi-ulang operand sebagai tipe
target (dispesifikasi di dalam kurung siku yang mengikuti katakunci reinterpret_cast). Pada Gambar 9.11,
Anda menggunakan reinterpret_cast
untuk mengkonversi sebuah pointer DataKlien
menjadi sebuah const char *, yang
menginterpretasi-ulang sebuah objek DataKlien
sebagai byte-byte yang akan dikeluarkan ke dalam sebuah file. Program pemrosesan
file akes-acak jarang sekali ditulis di dalam satu file. Seperti yang
didemonstrasikan pada contoh ini.
Program
Pemrosesan Kredit
Perhatikan
statemen permasalahan berikut:
Ciptakan sebuah program pemrosesan
kredit yang mampu menyimpan sebanyak-banyaknya 100 rekaman dengan panjang-tetap
untuk sebuah perusahaan yang dapat memiliki sampai 100 pelanggan. Setiap
rekaman harus memuat nomor akun yang berperan sebagai kunci rekaman, nama
akhir, nama pertama, dan saldo. Program yang dibuat harus dapat memperbarui
akun, menyisipkan akun baru, menghapus akun, dan menyisipkan semua rekaman akun
ke dalam sebuah file teks terformat untuk dicetak atau ditampilkan.
Pada
beberapa bagian selanjutnya, akan dikenalkan beberapa teknik untuk menciptakan
program pemrosesan kredit. Gambar 9.11 mengilustrasikan pembukaan sebuah file
akses-acak, mendefinisikan format rekaman menggunakan sebuah objek dari kelas DataKlien (Gambar 9.9 – 9.10) dan
menuliskan data ke dalam disk dalam format biner. Program ini menginisialisasi
semua 100 rekaman pada file kredit.dat
dengan objek-objek kosong, menggunakan fungsi write. Setiap objek kosong memuat 0 untuk nomor akun, string null
(yang direpresentasikan dengan tanda kutip kosong) untuk nama akhir dan nama
pertama, dan 0.0 untuk saldo. Setiap rekaman diinisialisasi dengan sejumlah
memori kosong dimana di dalamnya data akun akan disimpan.
Gambar 9.9 Definisi Kelas DataKlien
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// Gambar 9.9: DataKlien.h
// Definisi kelas DataKlien digunakan di
dalam Gambar 9.11 - 9.14.
#ifndef DATAKLIEN_H
#define DATAKLIEN_H
#include <string>
using namespace std;
class DataKlien
{
public:
// konstruktor default DataKlien
DataKlien( int = 0, string =
"", string = "", double = 0.0 );
// fungsi aksesor untuk nomorAkun
void setNomorAkun( int );
int getNomorAkun() const;
// fungsi aksesor untuk namaAkhir
void setNamaAkhir( string );
string getNamaAkhir() const;
// fungsi aksesor untuk namaPertama
void setNamaPertama( string );
string getNamaPertama() const;
// fungsi aksesor untuk saldo
void setSaldo( double );
double getSaldo() const;
private:
int nomorAkun;
char namaAkhir[ 15 ];
char namaPertama[ 10 ];
double saldo;
}; // akhir dari kelas DataKlien
#endif
|
Gambar 9.10 Kelas DataKlien Menyimpan Informasi
Kredit Pelanggan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
// Gambar 9.10: DataKlien.cpp
// Kelas DataKlien menyimpan informasi
kredit pelanggan.
#include <string>
#include "DataKlien.h"
using namespace std;
// konstruktor DataKlien default
DataKlien::DataKlien( int
nilaiNomorAkun,
string nilaiNamaAKhir, string nilaiNamaPertama, double
nilaiSaldo )
{
setNomorAkun( nilaiNomorAkun );
setNamaAkhir( nilaiNamaAKhir );
setNamaPertama( nilaiNamaPertama );
setSaldo( nilaiSaldo );
} // akhir dari DataKlien
// mendapatkan nilai nomor-akun
int DataKlien::getNomorAkun() const
{
return nomorAkun;
} // akhir dari fungsi getNomorAkun
// menetapkan nilai nomor-akun
void DataKlien::setNomorAkun( int
nilaiNomorAkun )
{
nomorAkun = nilaiNomorAkun; // harus
divalidasi
} // akhir dari fungsi setNomorAkun
// mendapatkan nama-akhir
string DataKlien::getNamaAkhir() const
{
return namaAkhir;
} // akhir dari fungsi getNamaAhir
// menetapkan nilai nama-akhir
void DataKlien::setNamaAkhir( string stringNamaAkhir )
{
// menyalin
sebanyak-banyaknya 15 karakter dari string ke namaAkhir
int panjang =
stringNamaAkhir.size();
panjang = ( panjang < 15 ? panjang : 14
);
stringNamaAkhir.copy(
namaAkhir, panjang );
namaAkhir[ panjang ] = '\0'; // menyambung karakter null ke dalam namaAkhir
} // akhir dari fungsi setNamaAkhir
// mendapatkan nilai nama-pertama
string DataKlien::getNamaPertama() const
{
return namaPertama;
} // akhir dari fungsi getNamaPertama
// menetapkan nilai nama-pertama
void DataKlien::setNamaPertama( string
stringNamaPertama )
{
// menyalin sebanyak-banyaknya 15 karakter
dari string ke namaPertama
int panjang =
stringNamaPertama.size();
panjang = ( panjang < 10 ? panjang : 9 );
stringNamaPertama.copy(
namaPertama, panjang );
namaPertama[ panjang ] = '\0'; // menyambung karakter null ke dalam namaPertama
} // akhir dari fungsi setNamaPertama
// mendapatkan nilai saldo
double DataKlien::getSaldo() const
{
return saldo;
} // akhir dari fungsi getSaldo
// menetapkan nilai saldo
void DataKlien::setSaldo( double nilaiSaldo )
{
saldo = nilaiSaldo;
} // akhir dari fungsi setSaldo
|
Objek
dari kelas string tidak memiliki
ukuran seragam, tetapi menggunakan memori yang teralokasi secara dinamis untuk
mengakomodasi string berbagai panjang. Anda harus menetapkan rekaman
panjang-tetap, jadi kelas DataKlien
menyimpan nama pertama dan nama akhir seorang pelanggan di dalam array char dengan panjang-tetap
(dideklarasikan pada Gambar 9.9, baris 32-33). Fungsi anggota setNamaAkhir (Gambar 9.10, baris 36-43)
dan setNamaPertama (Gambar 9.10,
baris 52-59) masing-masing menyalin karakter-karakter dari sebuah objek string ke dalam array char. Perhatikan fungsi setNamaAkhir. Baris 39 memanggil fungsi
anggota size (dari kelas string) untuk mendapatkan panjang atas stringNamaAkhir. Baris 40 memastikan
bahwa panjang lebih kecil dari 15
karakter, kemudian baris 41 menyalin sebanyak panjang karakter dari stringNamaAkhir
ke dalam array char, namaAkhir, menggunakan fungsi anggota copy. Fungsi anggota setNamaPertama
melakukan langkah-langkah yang sama untuk nama pertama.
Gambar
9.11, baris 11 menciptakan sebuah objek ofstream
untuk file kredit.dat. Argumen kedua
untuk konstruktor, ios::out|ios::binary,
mengindikasikan bahwa Anda sedang membuka file untuk keluaran dalam mode biner,
yang diperlukan jika Anda akan menulis rekaman dengan panjang-tetap. Baris
24-25 menyebabkan klienKosong untuk
dituliskan ke dalam file kredit.dat,
yang diasosiasikan dengan objek ofstream,
keluarKredit. Ingat bahwa operator sizeof menghasilkan nilai balik berupa
ukuran dalam byte atas objek yang diapit kurung. Argumen pertama pada fungsi write pada baris 24 harus bertipe const char *. Namun, tipe data dari &klienKosong adalah DataKlien *. Untuk mengkonversi &klienKosong menjadi const char *, baris 24 menggunakan operator reinterpret_cast, sehingga pemanggilan terhadap fungsi write dapat dikompilasi tanpa
menyebabkan error kompilasi.
Gambar 9.11 Menciptakan Sebuah File Akses_Acak
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// Gambar 9.11: gambar9_11.cpp
// Menciptakan sebuah file yang diakses
secara acak.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "DataKlien.h" // definisi kelas
DataKlien
using namespace std;
int main()
{
ofstream keluarKredit( "kredit.dat",
ios::out | ios::binary );
//
keluar program jika ofstream tidak dapat membuka file
if ( !keluarKredit )
{
cerr << "File tidak dapat
dibuka." << endl;
exit( 1 );
} // akhir dari if
DataKlien klienKosong; // konstruktor
mengnolkan setiap anggota data
// mengeluarkan 100 rekaman kosong ke file
for ( int i = 0; i < 100;
++i )
keluarKredit.write( reinterpret_cast<
const char * >( &klienKosong ),
sizeof( DataKlien ) );
} // akhir dari main
|
9.6 Menuliskan Data Secara Acak ke
dalam File Akses-Acak
Gambar
9.12 menuliskan data ke dalam file kredit.dat
dan menggunakan kombinasi dari fungsi seekp
dan write untuk menyimpan data pada
lokasi-lokasi yang spesifik di dalam file. Fungsi seekp menetapkan pointer put
untuk menunjuk ke posisi tertentu di dalam file, kemudian fungsi write mengeluarkan data. Baris 6
mencantumkan header DataKlien.h yang
didefinisikan di dalam Gambar 9.9, sehingga program dapat menggunakan
objek-objek DataKlien.
Gambar 9.12 Kelas DataKlien Menyimpan Informasi
Kredit Pelanggan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
// Gambar 9.12: gambar9_12.cpp
// Menulis ke dalam file akses-acak.
#include <iostream>
#include <fstream>
#include <stdlib>
#include "DataKlien.h" // definisi kelas
DataKlien
using namespace std;
int main()
{
int nomorAkun;
string namaAkhir;
string namaPertama;
double saldo;
fstream
keluarKredit( "kredit.dat", ios::in | ios::out
| ios::binary );
// keluar program jika fstream tidak dapat
membuka file
if ( !keluarKredit )
{
cerr << "File tidak dapat
dibuka." << endl;
exit( 1 );
} // akhir dari if
cout << "Masukkan
nomor akun (1 sampai 100, 0 untuk mengakhiri masukan)\n? ";
// meminta pengguna untuk menspesifikasi nomor
akun
DataKlien klien;
cin >> nomorAkun;
// pengguna memasukkan informasi, yang
disalin ke dalam file
while ( nomorAkun > 0 &&
nomorAkun <= 100 )
{
// pengguna memasukkan nama akhir, nama
pertama, dan saldo
cout << "Masukkan nama pertama,
nama akhir, dan saldo\n? ";
cin >> namaAkhir;
cin >> namaPertama;
cin >> saldo;
// menetapkan rekaman nomorAkun, namaAkhir,
namaPertama, dan saldo
klien.setNomorAkun( nomorAkun );
klien.setNamaAkhir( namaAkhir );
klien.setNamaPertama( namaPertama );
klien.setSaldo( saldo );
//
mencari posisi di dalam file dari rekaman
keluarKredit.seekp( (
klien.getNomorAkun() - 1 ) *
sizeof( DataKlien ) );
//
menulis informasi di dalam file
keluarKredit.write( reinterpret_cast<
const char * >( &klien ),
sizeof( DataKlien ) );
//
memampukan pengguna untuk memasukkan akun lain
cout << "Masukkan nomor akun\n?
";
cin >> nomorAkun;
} // akhir dari while
} // akhir dari main
|
Masukkan
nomor akun (1 sampai 100, 0 untuk mengakhiri masukan)
?
37
Masukkan
nama akhir, nama pertama, dan saldo
?
Tohonan
Robert 0.00
Masukkan
nomor akun
?
29
Masukkan
nama akhir, nama pertama, dan saldo
?
Chandra
Rico -24.54
Masukkan
nomor akun
?
96
Masukkan
nama akhir, nama pertama, dan saldo
?
Rotua Marolop
34.98
Masukkan
nomor akun
?
88
Masukkan
nama akhir, nama pertama, dan saldo
?
Duma
Eva 258.34
Masukkan
nomor akun
?
33
Masukkan
nama akhir, nama pertama, dan saldo
?
Meika
Rini 314.33
Masukkan
nomor akun
? 0
Baris
47-48 memposisikan pointer put untuk objek keluarKredit
agar menunjuk ke lokasi byte yang dihitung dengan
keluarKredit.seekp(
( klien.getNomorAkun() - 1 ) * sizeof( DataKlien ) );
Karena
nomor akun berada di antara 1 dan 100, 1 akan dikurangkan dari nomor akun
ketikan menghitung lokasi byte dari rekaman. Jadi, untuk rekaman 1, pointer put
ditetapkan menunjuk ke byte 0 di dalam file. Baris 16 menggunakan objek fstream, keluarKredit, untuk membuka file kredit.dat yang sudah ada. File dibuka untuk masukan dan keluaran
dalam mode biner dengan mengkombinasikan mode file-open ios::in, ios::out, dan ios::binary. Mode jamak ini
dikombinasikan dengan memisahkan setiap mode menggunakan operator OR bitwise
(|). Pembukaan file yang sudah ada, kredit.dat,
dengan cara ini memastikan bahwa program ini dapat memanipulasi rekaman yang
ditulis ke dalam file oleh program pada Gambar 9.11.
9.7 Membaca dari File Akses-Acak Secara
Sekuensial
Pada
bagian sebelumnya, Anda telah menciptakan sebuah file akses-acak dan menuliskan
data ke dalam file tersebut. Pada bagian ini, akan dikembangkan sebuah program
yang membaca file secara sekuensial dan menampilkan hanya rekaman-rekaman yang
memuat data. Program ini menghasilkan keuntungan tambahan. Pastikan Anda
mengetahui keuntungan tersebut di akhir bagian ini.
Fungsi
read dari kelas istream memasukkan sejumlah byte tertentu dari posisi sekarang di
dalam aliran tertentu ke dalam sebuah objek. Sebagai contoh, baris 30-31 dari
Gambar 9.13 membaca sejumlah byte yang dispesifikasi oleh sizeof(DataKlien) dari file yang diasosiasikan
dengan objek masukKredit dan
menyimpan data di dalam rekaman klien.
Fungsi read memerlukan argumen
pertama bertipe char *. Karena &klien bertipe DataKlien *, &klien
harus dicast menjadi char * menggunakan operator reinterpret_cast.
Gambar 9.13 Membaca File Akses-Acak Secara
Sekuensial
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
// Gambar 9.13: gambar9_13.cpp
// Membaca file akses-acak secara
sekuensial.
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdlib>
#include "DataKlien.h" // definisi kelas
DataKlien
using namespace std;
void keluaranBaris( ostream&, const
DataKlien & ); // prototipe
int main()
{
ifstream masukKredit( "kredit.dat",
ios::in | ios::binary );
// keluar program jika ifstream tidak bisa
membuka file
if ( !masukKredit )
{
cerr << "File tidak bisa
dibuka." << endl;
exit( 1 );
} // akhir dari if
cout << left << setw( 10 )
<< "Akun" << setw( 16 )
<< "Nama Akhir"
<< setw( 11 ) << "Nama Pertama" << left
<< setw( 10 ) << right
<< "Saldo" << endl;
DataKlien klien; // menciptakan rekaman
// membaca rekaman pertama dari file
masukKredit.read( reinterpret_cast<
char * >( &klien ),
sizeof( DataKlien ) );
//
membaca semua rekaman dari file
while(
masukKredit &&
!masukKredit.eof() )
{
// menampilkan rekaman
if ( klien.getNomorAkun() != 0 )
keluaranBaris( cout, klien );
//
membaca berikutnya dari file
masukKredit.read(
reinterpret_cast< char * >( &klien ),
sizeof( DataKlien ) );
} //
akhir dari while
} // akhir dari main
// menampilkan satu rekaman
void keluaranBaris( ostream &keluaran, const
DataKlien &rekaman )
{
keluaran << left << setw( 10
) << rekaman.getNomorAkun()
<< setw( 16 ) <<
rekaman.getNamaAkhir()
<< setw( 11 ) <<
rekaman.getNamaPertama()
<< setw( 10 ) <<
setprecision( 2 ) << right << fixed
<< showpoint <<
rekaman.getSaldo() << endl;
} // akhir dari fungsi keluaranBaris
|
Akun
Nama Akhir Nama Pertama Saldo
29
Chandra Rico -24.54
33
Meika Rini 314.33
37
Tohonan Robert 0.00
88
Duma Eva 258.34
96 Rotua Marolop 34.98
Gambar
9.13 membaca setiap rekaman di dalam file kredit.dat
secara sekuensial, memeriksa setiap rekaman untuk menentukan apakah memuat data
atau tidak, dan menampilkan keluaran terformat untuk rekaman-rekaman yang
memuat data. Kondisi pada baris 34 menggunakan fungsi anggota eof (dari kelas ios) untuk menentukan kapan end-of-file
dijumpai dan menyebabkan eksekusi atas statemen while berhenti. Juga, jika suatu error terjadi ketika membaca dari
file, loop akan berhenti, karena evaluasi terhadap masukKredit bernilai false.
Data yang dimasukkan dari file ditampilkan oleh fungsi keluaranBaris (baris 47-54), yang memerlukan dua argumen, suatu
objek ostream dan sebuah struktur DataKlien. Tipe parameter ostream ini menarik, karena sembarang
objek ostream (seperti cout) atau sembarang objek yang
diderivasi dari kelas ostream
(seperti objek bertipe ofstream) dapat disuplai sebagai argumen.
9.8 Studi Kasus: Program Pemrosesan
Transaksi
Sekarang
akan disajikan sebuah program pemrosesan transaksi (Gambar 9.14) menggunakan
suatu file akses-acak untuk melakukan pemrosesan secara instan. Program
menetapkan informasi akun bank. Program juga memperbarui akun yang sudah ada,
menambah akun baru, menghapus akun, dan menyimpan daftar terformat dari semua
akun di dalam suatu file teks. Diasumsukan bahwa program pada Gambar 9.11 telah
dieksekusi untuk menciptakan file kredit.dat
dan bahwa program pada Gambar 9.12 telah dieksekusi untuk menyisipkan data
awal.
Gambar 9.14 Membaca, Memperbarui, Menyisipkan, dan
Menghapus
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
|
// Gambar 9.14: gambar9_14.cpp
// Program ini
membaca sebuah file akses-acak secara sekuensial, memperbarui
// data yang
sebelumnya ditulis ke dalam file, menciptakan data yang akan ditempatkan
// ke dalam file,
dan menghapus data yang sebelumnya disimpan di dalam file.
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include "DataKlien.h" // definisi kelas
DataKlien
using namespace std;
int masukkanPilihan();
void ciptakanFileTeks( fstream& );
void perbaruiRekaman( fstream& );
void baruRekaman( fstream& );
void hapusRekaman( fstream& );
void keluaranBaris( ostream&, const
DataKlien & );
int getAkun( const char * const );
enum Pilihan { TAMPIL = 1, PERBARUI, BARU,
HAPUS, AKHIR };
int main()
{
//
membuka file untuk pembacaan dan penulisan
fstream masukKeluarKredit(
"kredit.dat", ios::in
| ios::out | ios::binary );
//
keluar program jika fstream tidak bisa membuka file
if( !masukKeluarKredit )
{
cerr << "File tidak dapat
bisa dibuka." << endl;
exit ( 1 );
} // akhir dari if
int pilihan; // menyimpan pilihan
pengguna
// memampukan pengguna untuk menentukan aksi
while ( ( pilihan = masukkanPilihan()
) != AKHIR )
{
switch ( pilihan )
{
case TAMPIL: // menciptakan file
teks dari file rekaman
ciptakanFileTeks( masukKeluarKredit );
break;
case PERBARUI: // memperbarui
rekaman
perbaruiRekaman( masukKeluarKredit );
break;
case BARU: // menciptakan rekaman
baruRekaman( masukKeluarKredit );
break;
case HAPUS: // menghapus rekaman
yang ada
hapusRekaman( masukKeluarKredit );
break;
default:
// menampilkan error jika pengguna tidak memilih pilihan
cerr << "Pilihan salah"
<< endl;
break;
} // akhir dari switch
masukKeluarKredit.clear(); // mereset
indikator end-of-file
} //
akhir dari while
} // akhir dari main
// memampukan pengguna memasukkan menu
pilihan
int masukkanPilihan()
{
// menampilkan opsi yang tersedia
cout << "\nMasukkan pilihan
Anda" << endl
<< "1 - simpan file teks
terformat yang memuat akun" << endl
<< " dinamakan \"tampil.txt\" untuk
ditampilkan" << endl
<< "2 - perbarui sebuah akun"
<< endl
<< "3 - tambah sebuah akun
baru" << endl
<< "4 - hapus sebuah akun"
<< endl
<< "5 - akhiri program\n?
";
int menuPilihan;
cin >> menuPilihan; // memasukkan
pilihan menu dari pengguna
return menuPilihan;
} // akhir dari fungsi masukkanPilihan
// menciptakan file teks terformat untuk
ditampilkan
void ciptakanFileTeks( fstream &bacaDariFile )
{
//
menciptakan file teks
ofstream keluarFileTampil(
"tampil.txt", ios::out );
// keluar program jika ofstream tidak bisa
menciptakan file
if( !keluarFileTampil )
{
cerr << "File tidak bisa
diciptakan." << endl;
exit( 1 );
} // akhir dari if
keluarFileTampil << left
<< setw( 10 ) << "Akun" << setw( 16 )
<< "Nama Akhir"
<< setw( 11 ) << "Nama Pertama" << right
<< setw( 10 ) <<
"Saldo" << endl;
//
menetapkan pointer posisi ke awal bacaDariFile
bacaDariFile.seekg( 0 );
//
membaca rekaman pertama dari file rekaman
DataKlien
klien;
bacaDariFile.read( reinterpret_cast<
char * >( &klien ),
sizeof( DataKlien ) );
//
menyalin semua rekaman dari file rekaman ke dalam file teks
while(
!bacaDariFile.eof()
)
{
// menuliskan satu rekaman ke dalam file
teks
if ( klien.getNomorAkun() != 0 ) //
lompati rekaman kosong
keluaranBaris( keluarFileTampil, klien
);
//
membaca rekaman berikutnya dari file rekaman
bacaDariFile.read( reinterpret_cast<
char * >( &klien ),
sizeof( DataKlien ) );
} //
akhir dari while
} // akhir dari fungsi ciptakanFileTeks
// memperbarui saldo di dalam rekaman
void perbaruiRekaman( fstream &perbaruiFile )
{
// mendapatkan nomor akun yang akan
diperbarui
int nomorAkun = getAkun( "Masukkan
akun yang akan diperbarui" );
//
menggerakkan pointer posisi ke rekaman yang tepat di dalam file
perbaruiFile.seekg( (
nomorAkun - 1 ) * sizeof( DataKlien ) );
//
membaca rekaman pertama dari file
DataKlien
klien;
perbaruiFile.read( reinterpret_cast<
char * >( &klien ),
sizeof( DataKlien ) );
//
memperbarui rekaman
if ( klien.getNomorAkun() != 0 )
{
keluaranBaris( cout, klien ); //
menampilkan rekaman
// meminta pengguna untuk menentukan
transaksi
cout << "\nMasukkan biaya
(+) atau bayar (-): ";
double transaksi; // biaya atau
bayar
cin >> transaksi;
// memperbarui rekaman saldo
double saldoLama =
klien.getSaldo();
klien.setSaldo( saldoLama + transaksi );
keluaranBaris( cout, klien ); //
menampilkan rekaman
// menggerakkan pointer posisi ke rekaman
yang sesuai di dalam file
perbaruiFile.seekp( (
nomorAkun - 1 ) * sizeof( DataKlien ) );
//
menulis rekaman terperbarui menggantikan rekaman lama di dalam file
perbaruiFile.write( reinterpret_cast<
const char * >( &klien ),
sizeof( DataKlien ) );
} //
akhir dari if
else // menampilkan error jika akun
tidak ada
cerr << "Akun #"
<< nomorAkun
<< " tidak memiliki
informasi." << endl;
} // akhir dari fungsi perbaruiRekaman
// menciptakan dan menyisipkan rekaman
void baruRekaman( fstream &sisipDalamFile )
{
// mendapatkan nomor akun untuk diciptakan
int nomorAkun = getAkun( "Masukkan
nomor akun baru" );
// menggerakkan pointer posisi ke rekaman
yang tepat di dalam file
sisipDalamFile.seekg( ( nomorAkun - 1 ) * sizeof(
DataKlien ) );
//
membaca rekaman dari file
DataKlien
klien;
sisipDalamFile.read( reinterpret_cast<
char * >( &klien ),
sizeof( DataKlien ) );
//
menciptakan rekaman, jika rekaman belum ada sebelumnya
if ( klien.getNomorAkun() == 0 )
{
string namaAkhir;
string namaPertama;
double saldo;
// pengguna memasukkan nama akhir, nama
pertama, dan saldo
cout << "Masukkan nama
akhir, nama pertama, dan saldo\n? ";
cin >> setw( 15 ) >>
namaAkhir;
cin >> setw( 10 ) >>
namaPertama;
cin >> saldo;
// menggunakan nilai-nilai untuk menginisi
nilai-nilai akun
klien.setNamaAkhir( namaAkhir );
klien.setNamaPertama( namaPertama );
klien.setSaldo( saldo );
klien.setNomorAkun( nomorAkun );
//
menggerakkan pointer posisi ke rekaman yang tepat
di dalam file
sisipDalamFile.seekp( (
nomorAkun - 1 ) * sizeof( DataKlien ) );
//
menyisikan rekaman di dalam file
sisipDalamFile.write( reinterpret_cast<
const char * >( &klien ),
sizeof( DataKlien ) );
} //
akhir dari if
else
// menampilkan error jika akun sudah ada
cerr
<< "Akun #" << nomorAkun
<<
" sudah memiliki informasi." << endl;
} // akhir dari fungsi baruRekaman
// menghapus rekaman yang sudah ada
void hapusRekaman( fstream &hapusDariFile )
{
//
mendapatkan nomor akun yang akan dihapus
int nomorAkun = getAkun( "Masukkan
akun yang akan dihapus" );
// menggerakkan pointer posisi ke rekaman
yang tepat di dalam file
hapusDariFile.seekg( (
nomorAkun - 1 ) * sizeof( DataKlien ) );
//
membaca rekaman dari file
DataKlien
klien;
hapusDariFile.read( reinterpret_cast<
char * >( &klien ),
sizeof( DataKlien ) );
//
menghapus rekaman, jika rekaman sudah ada di dalam file
if
( klien.getNomorAkun() != 0 )
{
DataKlien
kosongKlien; // menciptakan rekaman kosong
//
menggerakkan pointer posisi ke rekaman yang tepat
di dalam file
hapusDariFile.seekp( (
nomorAkun - 1 ) *
sizeof( DataKlien ) );
//
mengganti rekaman yang sudah ada dengan rekaman kosong
hapusDariFile.write(
reinterpret_cast< const char * >(
&kosongKlien ),
sizeof( DataKlien ) );
cout
<< "Akun #" << nomorAkun << " dihapus.\n";
} //
akhir dari if
else // menampilkan error jika
rekaman tidak ada
cerr << "Akun #"
<< nomorAkun << " kosong.\n";
} // akhir dari hapusRekaman
// menampilkan satu rekaman
void keluaranBaris( ostream &keluaran, const
DataKlien &rekaman )
{
keluaran << left << setw( 10 )
<< rekaman.getNomorAkun()
<< setw( 16 ) <<
rekaman.getNamaAkhir()
<< setw( 11 ) <<
rekaman.getNamaPertama()
<< setw( 10 ) << setprecision(
2 ) << right << fixed
<< showpoint <<
rekaman.getSaldo() << endl;
} // akhir dari fungsi keluaranBaris
//
mendapatkan nilai nomor-akun dari pengguna
int getAkun( const char * const prompt
)
{
int nomorAkun;
// mendapatkan nilai nomor-akun
do
{
cout << prompt << " (1 -
100): ";
cin >> nomorAkun;
} while ( nomorAkun < 1 ||
nomorAkun > 100 );
return nomorAkun;
} // akhir dari fungsi getAkun
|
Program
mempunyai lima opsi (opsi 5 diberikan untuk menghentikan program). Opsi 1
memanggil fungsi ciptakanFileTeks
untuk menyimpan daftar terformat atas semua informasi akun di dalam sebuah file
teks yang dinamakan tampil.txt.
Fungsi ciptakanFileTeks (baris 80 –
115) mengambil sebuah objek fstream
sebagai argumen untuk digunakan dalam memasukkan data dari file kredit.dat. Fungsi ciptakanFileTeks memanggil fungsi anggota read dari kelas istream
(baris 101-102) dan menggunakan teknik file-akses sekuensial pada Gambar 9.13
untuk mengeluarkan data ke file tampil.txt.
Perhatikan bahwa ciptakanFileTeks
menggunakan fungsi anggota seekg dari
kelas istream (baris 97) untuk memastikan bahwa pointer posisi ditempatkan di
awal file. Setelah memilih opsi 1, file tampil.txt memuat
Akun
Nama Akhir Nama Pertama Saldo
29
Chandra Rico -24.54
33
Meika Rini 314.33
37
Tohonan Robert 0.00
88
Duma Eva 258.34
96 Rotua Marolop 34.98
Opsi
2 memanggil perbaruiRekaman (baris
118 – 156) untuk memperbarui sebuah rekaman. Fungsi ini hanya memperbarui
rekaman yang sudah ada, jadi fungsi ini pertama-tama menentukan apakah rekaman
yang dispesifikasi kosong atau tidak. Baris 128 – 129 membaca data ke dalam
objek klien, menggunakan fungsi
anggota read dari kelas istream. Kemudian baris 132
membandingkan nilai yang dijadikan nilai balik oleh getNomorAkun dari objek klien dengan nol untuk menentukan apakah
rekaman memuat informasi atau tidak. Jika nilai tersebut nol, maka baris 154-155
menampilkan pesan error yang mengindikasikan bahwa rekaman kosong. Jika rekaman
memuat informasi, maka baris 134 menampilkan rekaman menggunakan fungsi keluaranBaris, baris 139 memasukkan
jumlah transaksi dan baris 142-151 menghitung saldo baru, dan menulis-ulang
rekaman ke dalam file. Keluaran untuk opsi adalah
Masukkan
akun untuk diperbarui (1 - 100): 37
37 Tohonan Robert 0.00
Masukkan
biaya (+) atau bayar (-): +87.99
37 Tohonan Robert 87.99
Opsi
3 memanggil fungsi baruRekaman (baris
159-201) untuk menambahkan sebuah rekaman baru ke dalam file. Jika pengguna
memasukkan sebuah nomor akun untuk akun yang sudah ada, baruRekaman menampilkan pesan error yang mengindikasikan bahwa akun
sudah ada (baris 199-200). Keluaran untuk opsi 3 adalah
Masukkan nomor akun baru
(1-100): 22
Masukkan nama akhir, nama
pertama, dan saldo
? Pratama Jodi 247.45
Opsi
memanggil fungsi hapusRekaman (baris
204-235) untuk menghapus sebuah rekaman dari file. Baris 207 mendesak pengguna
untuk memasukkan nomor akun. Hanya rekaman yang sudah ada yang akan dihapus,
jadi, jika akun yang dispesifikasi kosong, maka baris 234 akan menampilkan
pesan error. Jika akun sudah ada, baris 227-229 menginisialisasi-ulang akun
tersebut dengan menyalin sebuah rekaman kosong (kosongKlien) ke dalam file tersebut. Baris 231 menampilkan pesan
untuk menginformasikan pengguna bahwa rekaman telah dihapus. Keluaran untuk
opsi 4 adalah
Masukkkan akun untuk
dihapus (1-100): 29
Akun #29 dihapus.
Baris
25 membuka file kredit.dat dengan
menciptakan sebuah objek fstream
untuk pembacaan dan penulisan, menggunakan mode ios::in dan ios::out.
Kesimpulan
C++ memandang bahwa setiap file
hanyalah sebuah runtun byte. Setiap file diakhiri dengan penanda end-of-file
atau sejumlah byte tertentu yang direkam di dalam suatu struktur data sistem
operasi. Ketika sebuah file dibuka, suatu objek diciptakan, dan aliran
diasosiasikan dengan objek tersebut. Anda telah melihat bahwa objek cin, cout,
cerr, dan clog diciptakan ketika <iostream>
dicantumkan. Aliran yang diasosiasikan dengan objek menyediakan kanal
komunikasi antara program dan file atau divais tertentu. Sebagai contoh, objek cin (objek aliran masukan standard)
memampukan program untuk memasukkan data dari papanketik atau dari divais
lainnya, objek cout (objek aliran
keluaran standard) memampukan program untuk mengeluarkan data ke layar atau
divais lainnya, dan objek cerr dan clog (objek aliran error standard)
memampukan program untuk mengeluarkan error ke layar atau ke divais lainnya.
Seperti dinyatakan sebelumnya,
file dibuka dengan menciptakan objek ifstream,
ofstream, dan fstream. File
dibuka untuk keluaran, sehingga objek ofstream
yang diciptakan. Untuk suatu objek ofstream, mode file-open dapat berupa ios::out untuk mengeluarkan data ke
sebuah file atau berupa ios::app
untuk menyambungkan data ke akhir suatu file (tanpa memodifikasi data yang
sebelumnya sudah ada di dalam file itu). Data di dalam file yang sudah ada bila
dibuka dengan mode ios::out akan
dihapus. Jika file yang dispesifikasi belum ada, maka objek ofstream akan menciptakan file tersebut
dengan nama yang diberikan.
Fungsi anggota write pada kelas ostream mengeluarkan sejumlah tetap byte, dimulai dari lokasi
tertentu di dalam memori, ke aliran yang dispesifikasi. Ketika aliran
diasosiasikan dengan sebuah file, fungsi write
menulis data pada lokasi di dalam file yang dispesifikasi oleh pointer put.
Fungsi anggota read pada kelas
istream memasukkan sejumlah tetap
byte dari aliran yang dispesifikasi ke suatu area di dalam memori dimulai dari
alamat tertentu. Jika aliran diasosiasikan dengan sebuah file, maka fungsi read memasukkan byte-byte pada lokasi di
dalam file terspesifikasi oleh pointer get.
Latihan
1)
Asumsikan bahwa setiap statemen
berikut dapat diterapkan pada program yang sama.
a)
Tuliskan sebuah statemen yang
membuka file file_lama.dat untuk
masukan; gunakan objek ifstream yang
dinamakan masukFileLama.
b)
Tuliskan sebuah statemen yang
membuka file trans_file.dat; gunakan
objek ifstream yang dinama masukTrans.
c)
Tuliskan sebuah statemen yang
membuka file file_baru.dat; gunakan
objek ofstream yang dinamakan keluarFileBaru.
d)
Tuliskan sebuah statemen yang
membaca sebuah rekaman dari file file_lama.dat.
Rekaman memuat integer nomorAkun,
string nama, dan nilai pecahan saldoSekarang; gunakan objek ifstream yang dinamakan masukFileLama.
e)
Tuliskan sebuah statemen yang
membaca sebuah rekaman dari file trans_file.dat.
Rekaman memuat integer nomorAkun dan
nilai pecahan jumlahDollar; gunakan
objek ifstream yang dinamakan masukTrans.
f)
Tuliskan sebuah statemen yang
menulis sebuah rekaman ke dalam file file_baru.dat.
Rekaman memuat integer nomorAkun,
string nama, dan nilai pecahan saldoSekarang; gunakan objek ostream yang dinamakan keluarFileBaru.
2)
Tuliskan sederet statemen yang
melakukan hal-hal berikut. Asumsikan bahwa Anda telah mendefinisikan kelas Orang yang memuat anggota data private
char namaAkhir[15];
char namaPertama[10];
int usia;
int id;
dan fungsi-fungsi anggota public
//
fungsi aksesor untuk id
void setId( int );
int getId() const;
fungsi aksesor untuk namaTerakhir
void setNamaTerakhir( string );
string getNamaTerakhir() const;
fungsi aksesor untuk namaPertama
void setNamaPertama( string );
string getNamaPertama() const;
Fungsi aksesor untuk usia
void setUsia( int );
int getUsia) const;
Juga diasumsikan beberapa file akses-acak telah dibukan
secara benar.
a)
Inisialisasilah manusia.dat
dengan 100 rekaman yang menyimpan nilai-nilai namaTerakhir = “ “, namaPertama
= “ “, dan usia = 0.
b)
Masukkan 10 nama terakhir, nama
pertama, dan usia, dan tuliskan ke dalam file.
c)
Perbarui sebuah rekaman yang
sudah memuat informasi. Jika rekaman tidak memuat informasi, informasikan
kepada pengguna dengan pesan “Tidak ada
informasi”.
d)
Hapus sebuah rekaman yang
memuat informasi dengan menginisialisasi-ulang rekaman tersebut.
wow, sangat lengkap sekali
ReplyDeleteterimakasih~
ReplyDelete