Stream adalah abstraksi aliran data yang dibaca dan ditulis secara berurutan, sedangkan topik ini membahas pemakaiannya untuk berkas melalui FileStream, StreamReader, dan StreamWriter, serta peran buffering dalam mengatur kecepatan tulis baca. Pembahasan dimulai dari konsep aliran data, dilanjutkan menulis dan membaca berkas teks baris demi baris, dan ditutup dengan contoh penyalinan berkas melalui buffer. Setelah membacanya, operasi berkas berukuran besar dapat ditangani tanpa memuat seluruh isinya ke memori.
Stream sebagai aliran data#
Kelas Stream pada namespace System.IO adalah kelas dasar yang mewakili sumber atau tujuan data berupa urutan byte. Setiap implementasinya menyediakan operasi baca, tulis, dan pencarian posisi, sedangkan sumber datanya menentukan turunan yang dipakai, misalnya FileStream untuk berkas, MemoryStream untuk memori, dan kelas pada namespace jaringan untuk komunikasi antarperangkat. Posisi baca tulis dilacak pada property Position, sedangkan ukuran total tersedia pada property Length untuk stream yang mendukungnya. Karena stream menyimpan penangan ke sumber daya sistem operasi, setiap stream wajib ditutup dengan pernyataan using agar penangannya dilepas secara pasti. Tabel berikut memetakan kelas yang dipakai pada topik ini.
| Kelas | Beroperasi pada | Cocok untuk |
|---|---|---|
FileStream | byte pada berkas | kontrol penuh atas berkas dan buffer |
StreamReader | teks dari sebuah stream | membaca baris dan karakter |
StreamWriter | teks ke sebuah stream | menulis baris dan karakter |
Perbedaan byte dan teks menentukan kelas yang dipilih. FileStream memproses byte secara langsung dan tidak menafsirkan isinya, sedangkan StreamReader dan StreamWriter menambahkan lapisan penafsiran teks di atasnya, termasuk pengenalan penanda baris dan pemetaan karakter ke byte. Apabila isinya memang teks, lapisan tersebut menghemat kerja manual, sedangkan data biner seperti gambar dan arsip kompresi diproses sebagai byte agar tidak berubah.
Menulis dan membaca teks dengan StreamWriter dan StreamReader#
StreamWriter dan StreamReader membungkus sebuah stream byte menjadi aliran teks. StreamWriter mengonversi teks menjadi byte dengan encoding, yaitu aturan pemetaan karakter ke byte, dan metode WriteLine menambahkan satu baris beserta penanda barisnya. Sebaliknya, metode ReadLine pada StreamReader membaca satu baris dan mengembalikan null saat seluruh isi telah habis dibaca, sehingga perulangan while cukup berhenti pada kondisi tersebut. Contoh berikut menulis tiga baris catatan, membacanya kembali baris demi baris, lalu menghapus berkas percobaannya.
// Program: menulis dan membaca berkas teks lewat stream
using (StreamWriter penulis = new("bacaan.txt"))
{
for (int i = 1; i <= 3; i++)
{
penulis.WriteLine($"Catatan baris {i}");
}
}
using (StreamReader pembaca = new("bacaan.txt"))
{
string? baris;
while ((baris = pembaca.ReadLine()) is not null)
{
Console.WriteLine(baris);
}
}
File.Delete("bacaan.txt");
Console.WriteLine($"Berkas ada setelah dihapus: {File.Exists("bacaan.txt")}");Catatan baris 1
Catatan baris 2
Catatan baris 3
Berkas ada setelah dihapus: FalseUntuk menjalankan program tersebut dari folder topik, gunakan perintah berikut.
# aktifkan .NET yang terpasang di sandbox
export DOTNET_ROOT=/home/kal/.dotnet PATH=/home/kal/.dotnet:$PATH DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
# jalankan program contoh dari folder topik
dotnet run --file Program.csCatatan baris 1
Catatan baris 2
Catatan baris 3
Berkas ada setelah dihapus: FalseKedua pernyataan using di atas membuat StreamWriter dan StreamReader yang menampung FileStream secara internal tanpa disebutkan secara eksplisit. Blok penulis selesai lebih dahulu, dan penutupan using-nya memastikan seluruh teks benar-benar tertulis ke disk sebelum berkas dibaca. Perulangan pada blok pembaca berhenti ketika ReadLine mengembalikan null, yang berarti posisi baca sudah mencapai akhir berkas. Untuk berkas berukuran kecil, metode ReadToEnd mengembalikan seluruh isi sebagai satu string tanpa perulangan, dan pilihan antara keduanya mengikuti kebutuhan pemrosesan baris demi baris atau sekali jalan. Kedua penangan kemudian dilepas secara otomatis oleh pernyataan using, sehingga Delete dapat dijalankan tanpa berkas yang masih terbuka.
Buffering#
Buffering adalah penampungan data sementara di memori sebelum data dipindahkan ke tujuan akhirnya. Penulisan ke disk tergolong operasi yang mahal, sehingga StreamWriter mengumpulkan teks di buffer internalnya dan menuliskannya ke disk dalam potongan besar sekali jalan. Perilaku ini mengubah kapan data benar-benar sampai ke berkas, dan property AutoFlush mengatur batasnya. Apabila AutoFlush dinyatakan false, isi buffer baru sampai ke disk ketika buffer penuh atau ketika stream dibuang. Contoh berikut menunjukkan perbedaannya.
// Contoh: StreamWriter menampung isi di buffer sebelum di-flush
FileStream aliran = File.Create("log.txt");
StreamWriter penulis = new(aliran) { AutoFlush = false };
penulis.WriteLine("entri 1");
Console.WriteLine($"Ukuran sebelum Dispose: {new FileInfo("log.txt").Length} byte");
penulis.Dispose();
Console.WriteLine($"Ukuran setelah Dispose: {new FileInfo("log.txt").Length} byte");
File.Delete("log.txt");Ukuran sebelum Dispose: 0 byte
Ukuran setelah Dispose: 8 byteSatu baris entri 1 berukuran 8 byte, tetapi sebelum Dispose dipanggil ukuran berkas masih 0 karena teksnya masih tertahan di buffer. Pemanggilan Dispose menyiram isi buffer ke disk lebih dahulu, baru kemudian menutup stream-nya. Metode Flush tersedia untuk penyiraman manual pada titik tertentu tanpa menutup stream, dan AutoFlush = true menyiram buffer setiap kali WriteLine dipanggil dengan biaya operasi disk yang lebih sering.
Argumen bufferSize pada konstruktor FileStream menetapkan ukuran buffer internalnya dalam byte, dan nilai defaultnya 4096. Ukuran yang lebih besar mengurangi jumlah panggilan sistem untuk berkas berukuran besar dengan biaya memori yang lebih tinggi, sedangkan nilai defaultnya sudah pas untuk kebanyakan kasus. StreamReader dan StreamWriter menerima argumen serupa, sehingga penyetelan buffer dilakukan pada titik yang sama dengan pembukaan berkas.
Buffer juga dapat diatur secara manual pada FileStream untuk penyalinan byte. Contoh berikut menyalin isi berkas berukuran 10000 byte dengan buffer berukuran 4096 byte, sehingga perpindahan data terjadi dalam tiga kali baca dan tiga kali tulis.
// Contoh: menyalin berkas dengan buffer 4 kilobyte
File.WriteAllBytes("sumber.bin", Enumerable.Range(0, 10000).Select(i => (byte)(i % 256)).ToArray());
using (FileStream sumber = File.OpenRead("sumber.bin"))
using (FileStream tujuan = File.Create("salinan.bin"))
{
byte[] buffer = new byte[4096];
int terbaca;
while ((terbaca = sumber.Read(buffer, 0, buffer.Length)) > 0)
{
tujuan.Write(buffer, 0, terbaca);
}
}
Console.WriteLine($"Sumber: {new FileInfo("sumber.bin").Length} byte");
Console.WriteLine($"Salinan: {new FileInfo("salinan.bin").Length} byte");
File.Delete("sumber.bin");
File.Delete("salinan.bin");Sumber: 10000 byte
Salinan: 10000 byteMetode Read mengisi buffer dan mengembalikan jumlah byte yang benar-benar terbaca, yang nilainya menurun pada pembacaan terakhir karena sisa berkasnya lebih kecil daripada buffer. Perulangan berhenti ketika Read mengembalikan 0, dan Write hanya menulis sebanyak byte yang terbaca. Pola baca-tulis dengan buffer semacam ini menjadi dasar bagi metode siap pakai seperti CopyTo yang melakukan penyalinan yang sama dengan satu pemanggilan.
Ringkasan#
Streamadalah kelas dasar aliran byte, sedangkanStreamReaderdanStreamWritermembungkusnya menjadi aliran teks dengan encoding tertentu.ReadLinemengembalikannulldi akhir stream, dan seluruh stream wajib ditutup denganusingagar penangan sumber dayanya terlepas.- Buffering menahan data di memori sampai buffer penuh, stream dibuang, atau
Flushdipanggil, sehingga operasi disk berkurang. - Penyalinan byte berjalan dengan perulangan
ReaddanWritedi atas larik buffer, danCopyTomembungkus pola yang sama.
Referensi#
- File and Stream I/O — Microsoft Learn — Ikhtisar I/O berkas dan stream pada .NET beserta kelas-kelas utamanya.
- FileStream class — Microsoft Learn
— Referensi kelas
FileStreamtermasuk argumen ukuran buffer pada konstruktornya.