Langsung ke konten
KamusNgoding
Menengah Csharp 3 menit baca

File I/O di C#: Membaca dan Menulis File dengan Mudah

#csharp #file #streamreader #streamwriter #file-class #path #async #fileinfo #directory

C# menawarkan dua pendekatan untuk file I/O: File class dengan metode statis yang sederhana untuk operasi satu baris, dan StreamReader/StreamWriter untuk kontrol lebih detail. Keduanya bekerja dengan using statement (dari artikel exception handling) untuk memastikan resource selalu dibersihkan dengan benar.

File Class — Operasi Sederhana Satu Baris

using System.IO;

// Tulis semua teks (buat baru atau timpa yang ada)
File.WriteAllText("catatan.txt", "Halo, dunia!\nBaris kedua.");

// Tulis banyak baris
File.WriteAllLines("daftar.txt", new[] { "Apel", "Mangga", "Jeruk" });

// Baca semua teks sekaligus
string isi = File.ReadAllText("catatan.txt");
Console.WriteLine(isi);
// Output:
// Halo, dunia!
// Baris kedua.

// Baca sebagai array baris
string[] baris = File.ReadAllLines("daftar.txt");
foreach (string b in baris)
    Console.WriteLine(b);

// Tambahkan teks di akhir (append)
File.AppendAllText("log.txt", $"[{DateTime.Now}] Aplikasi dijalankan\n");

// Cek keberadaan file
if (File.Exists("data.txt"))
    Console.WriteLine("File ada!");
else
    Console.WriteLine("File tidak ditemukan.");

// Salin, pindah, hapus
File.Copy("sumber.txt", "tujuan.txt", overwrite: true);
File.Move("lama.txt", "baru.txt");
File.Delete("temp.txt");

StreamReader dan StreamWriter — Kontrol Penuh

Gunakan stream untuk file besar (baca baris per baris tanpa load semua ke memori):

using System.IO;

// StreamWriter — tulis ke file
using (var writer = new StreamWriter("laporan.txt"))
{
    writer.WriteLine("=== Laporan Penjualan ===");
    writer.WriteLine($"Tanggal: {DateTime.Now:yyyy-MM-dd}");
    writer.WriteLine();

    var produk = new[] {
        ("Laptop", 15_000_000, 3),
        ("Mouse", 250_000, 10),
        ("Keyboard", 750_000, 7)
    };

    foreach (var (nama, harga, qty) in produk)
    {
        writer.WriteLine($"{nama,-15} Rp {harga,10:N0}  x{qty}  = Rp {harga * qty:N0}");
    }
}

// StreamReader — baca file besar baris per baris
using (var reader = new StreamReader("laporan.txt"))
{
    string? baris;
    int nomor = 1;
    while ((baris = reader.ReadLine()) != null)
    {
        Console.WriteLine($"{nomor++,3}: {baris}");
    }
}

Path — Manipulasi Jalur File dengan Aman

using System.IO;

// Path.Combine — gabung path lintas platform (/ di Linux, \ di Windows)
string folder = Path.Combine("data", "pengguna");
string file = Path.Combine(folder, "profil.json");
Console.WriteLine(file);
// Windows: data\pengguna\profil.json
// Linux: data/pengguna/profil.json

// Komponen path
string fullPath = @"C:\Proyek\src\program.cs";
Console.WriteLine(Path.GetFileName(fullPath));       // program.cs
Console.WriteLine(Path.GetFileNameWithoutExtension(fullPath)); // program
Console.WriteLine(Path.GetExtension(fullPath));      // .cs
Console.WriteLine(Path.GetDirectoryName(fullPath));  // C:\Proyek\src

// Path sementara dan khusus
string temp = Path.GetTempFileName();     // File temp di folder sistem
string tmpFolder = Path.GetTempPath();    // Folder temp sistem
string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

// Buat direktori jika belum ada
Directory.CreateDirectory(folder);
Console.WriteLine($"Folder '{folder}' siap digunakan.");

Async File I/O — Tidak Memblokir Thread

Untuk aplikasi yang responsif (web, desktop), gunakan async I/O:

using System.IO;

// Async baca — tidak memblokir thread utama
async Task<string> BacaFileAsync(string path)
{
    if (!File.Exists(path))
        throw new FileNotFoundException($"File tidak ditemukan: {path}");

    return await File.ReadAllTextAsync(path);
}

// Async tulis
async Task TulisLogAsync(string path, string pesan)
{
    string entri = $"[{DateTime.Now:HH:mm:ss}] {pesan}\n";
    await File.AppendAllTextAsync(path, entri);
}

// Penggunaan
async Task JalankanAsync()
{
    // Tulis dan baca secara async
    await TulisLogAsync("app.log", "Aplikasi dimulai");
    await TulisLogAsync("app.log", "Proses data...");
    await TulisLogAsync("app.log", "Selesai");

    string isiLog = await BacaFileAsync("app.log");
    Console.WriteLine(isiLog);
}

Praktik: Aplikasi Buku Kontak dengan File

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class Kontak
{
    public string Nama { get; set; }
    public string Telepon { get; set; }
    public string Email { get; set; }
}

class BukuKontak
{
    private readonly string _path;
    private List<Kontak> _kontak = new();

    public BukuKontak(string path)
    {
        _path = path;
        Muat();
    }

    // Muat data dari file
    private void Muat()
    {
        if (!File.Exists(_path)) return;

        foreach (string baris in File.ReadAllLines(_path))
        {
            if (string.IsNullOrWhiteSpace(baris)) continue;
            string[] bagian = baris.Split('|');
            if (bagian.Length < 3) continue;

            _kontak.Add(new Kontak
            {
                Nama = bagian[0],
                Telepon = bagian[1],
                Email = bagian[2]
            });
        }
        Console.WriteLine($"Memuat {_kontak.Count} kontak dari {_path}");
    }

    // Simpan semua kontak ke file
    private void Simpan()
    {
        var baris = _kontak.Select(k => $"{k.Nama}|{k.Telepon}|{k.Email}");
        File.WriteAllLines(_path, baris);
    }

    public void Tambah(string nama, string telepon, string email)
    {
        _kontak.Add(new Kontak { Nama = nama, Telepon = telepon, Email = email });
        Simpan();
        Console.WriteLine($"Kontak '{nama}' berhasil ditambahkan.");
    }

    public void Tampilkan()
    {
        if (_kontak.Count == 0)
        {
            Console.WriteLine("Buku kontak kosong.");
            return;
        }
        Console.WriteLine($"\n{'Nama',-20} {'Telepon',-15} {'Email',-25}");
        Console.WriteLine(new string('-', 62));
        foreach (var k in _kontak.OrderBy(k => k.Nama))
            Console.WriteLine($"{k.Nama,-20} {k.Telepon,-15} {k.Email,-25}");
    }

    public bool Hapus(string nama)
    {
        int sebelum = _kontak.Count;
        _kontak = _kontak.Where(k => !k.Nama.Equals(nama, StringComparison.OrdinalIgnoreCase)).ToList();
        if (_kontak.Count < sebelum) { Simpan(); return true; }
        return false;
    }
}

// Penggunaan
var buku = new BukuKontak("kontak.txt");
buku.Tambah("Ali Akbar", "08123456789", "ali@email.com");
buku.Tambah("Budi Santoso", "08987654321", "budi@email.com");
buku.Tambah("Siti Rahayu", "08111222333", "siti@email.com");
buku.Tampilkan();
// Output:
// Nama                 Telepon         Email
// --------------------------------------------------------------
// Ali Akbar            08123456789     ali@email.com
// Budi Santoso         08987654321     budi@email.com
// Siti Rahayu          08111222333     siti@email.com

Pertanyaan yang Sering Diajukan

Kapan menggunakan File.ReadAllText vs StreamReader?

Gunakan File.ReadAllText / File.ReadAllLines untuk file kecil-sedang (< beberapa MB) karena lebih ringkas. Gunakan StreamReader dengan ReadLine() untuk file besar karena membaca satu baris sekaligus tanpa memuat seluruh file ke memori — penting untuk file log besar atau data CSV jutaan baris.

Mengapa Path.Combine lebih baik daripada concatenasi string biasa?

Path.Combine("folder", "file.txt") secara otomatis menggunakan separator yang benar untuk sistem operasi (backslash di Windows, forward slash di Linux/Mac). Concatenasi manual "folder" + "\\" + "file.txt" akan gagal di Linux. Selain itu, Path.Combine menangani edge case seperti trailing slash yang tidak konsisten.

Apakah perlu try/catch saat menggunakan File class?

Ya. Operasi file bisa gagal karena banyak alasan: file tidak ada (FileNotFoundException), izin tidak cukup (UnauthorizedAccessException), disk penuh (IOException). Selalu tangkap IOException (atau lebih spesifik) di sekitar operasi file, terutama untuk path yang berasal dari input pengguna atau konfigurasi eksternal.

Apa perbedaan using untuk StreamReader dan await using untuk async stream?

using (synchronous) memanggil Dispose() secara sinkron. await using (C# 8+) memanggil DisposeAsync() secara asinkron — lebih baik untuk resource yang operasi Dispose-nya bisa async (seperti menutup koneksi jaringan). Untuk file lokal biasa, perbedaannya minimal — keduanya bekerja dengan baik.

Kesimpulan

OperasiCara C#Kapan Digunakan
Baca semuaFile.ReadAllText(path)File kecil-sedang
Baca per barisStreamReader.ReadLine()File besar
Tulis baruFile.WriteAllText(path, isi)Sederhana
AppendFile.AppendAllText(path, isi)Log file
Async bacaawait File.ReadAllTextAsync(path)Web/desktop apps
Path amanPath.Combine(folder, file)Selalu
Cek adaFile.Exists(path)Sebelum baca

Artikel sebelumnya: Exception Handling di C# — try/catch/finally dan using.

Selamat! Kamu sudah menguasai fondasi C# dari variabel hingga file I/O. Untuk langkah selanjutnya, pelajari C# dalam konteks web dengan ASP.NET Core — framework web server-side yang powerful.

Artikel Terkait