Membaca dan menulis

Membaca berkas baris demi baris dengan while, menulis ke berkas melalui filehandle, dan menutupnya dengan close.

Daftar isi

Topik ini membahas pertukaran data antara skrip Perl dan berkas teks melalui filehandle yang sudah dibuka. Pembahasan mencakup pembacaan baris demi baris dengan perulangan while, penghapusan karakter baris baru dengan chomp, penulisan data melalui print dengan handle, penambahan isi dengan mode tambah, serta penutupan berkas dengan close. Setelah seluruh contoh diikuti, skrip dapat menyimpan daftar data ke berkas, membacanya kembali, dan menambahkan butir baru tanpa menghapus isi lama.

Membaca baris demi baris dengan while#

Membaca isi berkas dilakukan baris demi baris melalui handle yang dibuka dengan mode baca. Operator bacanya ditulis sebagai nama handle yang dikelilingi tanda kurung sudut, yaitu <$baca> untuk handle bernama $baca. Setiap kali digunakan, operator itu mengembalikan satu baris berikutnya dari berkas, lengkap dengan karakter baris baru di akhirnya. Ketika seluruh isi sudah habis, operator mengembalikan nilai tidak terdefinisi (undef), dan nilai itulah yang menghentikan perulangan.

Bentuk while (my $baris = <$baca>) menggabungkan pembacaan dan pengujian akhir berkas dalam satu baris kode. Variabel $baris dideklarasikan untuk setiap putaran, sehingga cakupannya terbatas di dalam blok perulangan. Skrip berikut menyiapkan berkas log empat baris, kemudian membacanya dan memberi nomor pada setiap baris:

perl baca-baris.pl
#!/usr/bin/perl
use strict;
use warnings;

# menyiapkan berkas log berisi empat baris
open(my $tulis, '>', 'log-suhu.txt')
    or die "Tidak dapat membuka log-suhu.txt: $!";

print $tulis "2024-05-06 07:00 24.1\n";
print $tulis "2024-05-06 08:00 25.3\n";
print $tulis "2024-05-06 09:00 27.0\n";
print $tulis "2024-05-06 10:00 28.4\n";

close($tulis);

# membaca berkas baris demi baris
open(my $baca, '<', 'log-suhu.txt')
    or die "Tidak dapat membuka log-suhu.txt: $!";

my $nomor = 0;
while (my $baris = <$baca>) {
    chomp $baris;
    $nomor = $nomor + 1;
    print "$nomor: $baris\n";
}

close($baca);
text Keluaran
1: 2024-05-06 07:00 24.1
2: 2024-05-06 08:00 25.3
3: 2024-05-06 09:00 27.0
4: 2024-05-06 10:00 28.4

Variabel $nomor bertambah satu setiap putaran, sehingga keluarannya memperlihatkan pembacaan berjalan dari baris pertama sampai terakhir. Setiap pemanggilan <$baca> selalu melanjutkan dari titik baca terakhir, bukan mengulang dari awal berkas. Bentuk <$baca> sebenarnya adalah operator diamond (<>) yang diarahkan pada handle tertentu. Diamond tanpa handle, yaitu <> polos, membaca dari berkas yang namanya diberikan pada command line ketika skrip dijalankan, atau dari masukan standar bila tidak ada nama berkasnya. Bentuk tanpa handle itu lazim pada skrip penyaring teks pendek, sedangkan handle eksplisit lebih jelas digunakan bila berkas yang dimaksud sudah ditentukan di dalam program.

Menghapus baris baru dengan chomp#

Setiap baris yang dibaca membawa karakter baris baru \n di akhirnya, karena operator baca mengembalikan baris apa adanya dari berkas. Karakter itu dihapus dengan fungsi chomp sebelum barisnya diproses lebih lanjut. Fungsi chomp menghapus satu karakter baris baru di akhir string bila ada, dan membiarkan stringnya utuh bila tidak ada, sehingga aman dipanggil pada setiap baris hasil pembacaan tanpa perlu pengujian tersendiri.

Pola yang berulang pada contoh adalah chomp $baris tepat setelah baris dibaca, kemudian \n ditulis sendiri pada pernyataan print. Pola ini menjaga keluaran tetap terkendali, karena tanda baris baru ditambahkan sekali dan hanya di tempat yang diinginkan.

perl Pola standar membaca satu baris
# tidak-dijalankan
while (my $baris = <$fh>) {
    chomp $baris;
    # $baris kini bebas dari karakter baris baru
    print "$baris\n";
}

Apabila chomp dilewatkan tetapi \n tetap ditulis pada print, setiap baris data diikuti satu baris kosong, dan daftar yang dihasilkan berselang-seling dengan baris kosong. Apabila chomp dilewatkan dan \n tidak ditulis, seluruh butir justru menyatu menjadi satu baris panjang. Dua kemungkinan itu menunjukkan bahwa penempatan baris baru adalah bagian dari kendali keluaran, bukan urusan yang diserahkan pada kebetulan.

Menulis ke berkas menggunakan print yang diikuti handle tulis. Bentuknya print $tulis "teks\n", yaitu handle ditulis langsung setelah kata print tanpa koma, lalu teks yang dikirim. Tanpa handle, print mengirim argumennya ke STDOUT, sedangkan dengan handle, teksnya diarahkan ke berkas yang terhubung dengan handle tersebut. Teks yang dikirim dipertahankan persis sebagaimana ditulis, sehingga \n tetap perlu ditulis sendiri pada akhir setiap butir.

Skrip berikut menulis daftar belanja tiga butir ke berkas baru, menutup handle penulisnya, kemudian membuka ulang berkasnya untuk memeriksa isinya:

perl tulis-daftar.pl
#!/usr/bin/perl
use strict;
use warnings;

# menulis daftar belanja ke berkas baru
open(my $tulis, '>', 'daftar-belanja.txt')
    or die "Tidak dapat membuka daftar-belanja.txt: $!";

print $tulis "beras\n";
print $tulis "telur\n";
print $tulis "minyak goreng\n";

close($tulis);

# membaca kembali isi berkas untuk diperiksa
open(my $baca, '<', 'daftar-belanja.txt')
    or die "Tidak dapat membuka daftar-belanja.txt: $!";

while (my $baris = <$baca>) {
    chomp $baris;
    print "- $baris\n";
}

close($baca);
text Keluaran
- beras
- telur
- minyak goreng

Pemeriksaan pada contoh sengaja membuka ulang berkasnya dengan mode baca setelah handle penulisnya ditutup dengan close. Urutan ini menjamin seluruh data benar-benar sudah sampai ke berkas, sehingga isi yang terbaca adalah isi yang baru ditulis. Pola tulis, tutup, baca ulang inilah cara termudah menguji bahwa penulisan berkas berjalan sesuai harapan.

Menambah isi dengan mode >>#

Mode tambah >> membuka berkas tanpa menghapus isi lamanya, dan setiap data yang ditulis diletakkan setelah baris terakhir. Mode ini digunakan bila berkas berperan sebagai catatan yang terus bertambah, misalnya daftar belanja yang diperbarui dari waktu ke waktu atau log sensor yang menumpuk setiap jam pencatatan. Handle yang dibuka dengan mode ini menulis di bagian akhir, tetapi tetap tidak boleh digunakan untuk membaca. Pemisahan arah inilah yang menjaga isi lama, karena titik tulisnya berada di bagian akhir sedangkan isi awalnya tidak disentuh sama sekali. Mode ini juga lazim digunakan oleh beberapa proses pada berkas yang sama, karena penulisan baru tidak menghapus hasil yang sudah masuk lebih dahulu.

Skrip berikut menyiapkan isi awal daftar agar dapat dijalankan berulang kali, menambahkan dua butir dengan mode >>, kemudian menampilkan seluruh isinya:

perl tambah-daftar.pl
#!/usr/bin/perl
use strict;
use warnings;

# menyiapkan isi awal berkas agar skrip dapat dijalankan berulang kali
open(my $tulis, '>', 'daftar-belanja.txt')
    or die "Tidak dapat membuka daftar-belanja.txt: $!";

print $tulis "beras\n";
print $tulis "telur\n";
print $tulis "minyak goreng\n";

close($tulis);

# menambahkan dua butir baru tanpa menghapus isi lama
open(my $tambah, '>>', 'daftar-belanja.txt')
    or die "Tidak dapat membuka daftar-belanja.txt: $!";

print $tambah "gula\n";
print $tambah "teh\n";

close($tambah);

# menampilkan seluruh isi berkas setelah penambahan
open(my $baca, '<', 'daftar-belanja.txt')
    or die "Tidak dapat membuka daftar-belanja.txt: $!";

while (my $baris = <$baca>) {
    chomp $baris;
    print "- $baris\n";
}

close($baca);
text Keluaran
- beras
- telur
- minyak goreng
- gula
- teh

Keluaran memuat lima butir, yaitu tiga butir awal dan dua butir tambahan, dengan urutan yang mengikuti urutan penulisan. Data baru selalu menyambung setelah baris terakhir, sehingga isi lama tidak pernah bergeser atau tertimpa.

Kesalahan umum#

Kesalahan pertama adalah melewatkan chomp pada baris hasil pembacaan. Karakter baris baru yang tersisa membuat butir-butir tampil berseling dengan baris kosong atau memengaruhi perbandingan string, misalnya perbandingan "teh\n" ne "teh" yang hasilnya benar padahal isi teksnya sama. Panggil chomp segera setelah baris dibaca.

Kesalahan kedua adalah membaca dari handle yang dibuka dengan mode tulis. Handle semacam itu tidak menyediakan data untuk dibaca, sehingga perulangan while selesai tanpa satu baris pun terbaca dan tanpa pesan apa pun. Berkas yang hendak dibaca harus dibuka ulang dengan mode <, sebagaimana dilakukan pada seluruh skrip contoh.

Kesalahan ketiga adalah menganggap print menambahkan baris baru secara otomatis. Tanpa \n yang ditulis eksplisit, butir-butir yang dikirim dalam beberapa pernyataan menyatu menjadi satu baris panjang di dalam berkas. Berkas seperti itu tetap dapat dibaca oleh program, tetapi strukturnya tidak lagi per baris, sehingga perulangan pembacaan menghasilkan satu putaran berisi seluruh isi. Tulis \n pada akhir setiap butir agar berkasnya tetap terbaca baris demi baris.

Ringkasan#

  • while (my $baris = <$fh>) membaca berkas baris demi baris sampai akhir berkas tercapai, dan setiap baris masih membawa karakter baris baru.
  • chomp $baris menghapus karakter baris baru di akhir string bila ada, sehingga barisnya aman diproses dan dicetak ulang.
  • print $fh "teks\n" menulis ke berkas melalui handle tanpa koma setelahnya, sedangkan mode >> menambah data di bagian akhir tanpa menghapus isi lama.
  • close($fh) memastikan data tulisan benar-benar sampai ke berkas sebelum berkas itu dibaca kembali atau dibiarkan.

Referensi#