Namespace System.IO punya semua yang dibutuhkan untuk bekerja dengan file. Untuk sebagian besar tugas, satu panggilan pada class static File sudah cukup: method itu membuka file, membaca atau menulisnya, lalu menutupnya. Untuk file besar atau kendali yang lebih rinci, pakai StreamReader dan StreamWriter. Tambahkan using System.IO; ke setiap contoh di bawah.
Menulis dan membaca seluruh isi file
File.WriteAllText membuat file (atau menimpa file yang sudah ada) dengan teks yang kamu berikan. File.ReadAllText mengembalikan seluruh isi file sebagai satu string. File.AppendAllText menambahkan ke bagian akhir:
Output:
True
Buy coffee
Call the dentist
Renew passport
43 characters
Start over
False
Path relatif seperti "notes.txt" di-resolve terhadap direktori kerja saat ini milik proses, yang tidak selalu folder tempat programmu berada (sebuah service, tugas terjadwal, atau test runner bisa dimulai dari tempat lain). Untuk file yang dikirim bersama aplikasimu, susun path-nya dari AppContext.BaseDirectory.
WriteAllText menimpa tanpa bertanya. Kalau kehilangan isi lama itu berarti masalah, cek File.Exists dulu atau tulis ke file sementara lalu pindahkan ke tempatnya dengan File.Move.
Baris: WriteAllLines, ReadAllLines, dan ReadLines
File berisi record, satu per baris, cukup umum sampai punya method sendiri. WriteAllLines menulis setiap string diikuti baris baru; ReadAllLines mengembalikan string[]:
Output:
4 lines, header: date,product,amount
Total: 45.60
2026-03-03,Stapler,7.00
Perbedaan kedua pembaca ini penting untuk ukuran file. ReadAllLines membaca seluruh file ke memori sebelum kembali. ReadLines mengembalikan IEnumerable<string> yang lazy dan membaca seiring iterasimu, jadi foreach pada log berukuran 5 GB hanya memakai beberapa kilobyte. Dengan LINQ, ReadLines(...).Where(...).Take(10) berhenti membaca setelah mendapat sepuluh baris yang cocok.
Mem-parse CSV dengan Split(',') berfungsi untuk file sederhana yang kamu buat sendiri. CSV di dunia nyata punya field bertanda kutip yang berisi koma; pakai library seperti CsvHelper untuk itu.
Perhatikan InvariantCulture di decimal.Parse: tanpa itu, mem-parse "4.50" di mesin yang diatur ke bahasa Jerman atau Portugis akan membaca titik sebagai pemisah ribuan.
StreamWriter dan StreamReader
Method File membuka dan menutup file di setiap panggilan. Untuk menulis banyak potongan seiring waktu, atau membaca file yang terlalu besar untuk memori tanpa LINQ, buka sebuah stream sekali saja. Selalu bungkus dengan using supaya stream ditutup meskipun ada exception; writer yang tidak ditutup mungkin tidak pernah mengosongkan buffer-nya, dan file-nya berakhir kosong atau terpotong.
Output:
2: WARN disk 85% full
4: ERROR connection refused
ReadLine mengembalikan null di akhir file, dan itulah yang mengakhiri loop while. StreamWriter menampung output-nya di buffer dan menuliskannya ke disk saat buffer penuh, saat kamu memanggil Flush(), dan saat writer di-dispose; blok using menjamin yang terakhir itu terjadi.
Sejak C# 8, deklarasi using men-dispose stream di akhir blok yang melingkupinya, tanpa kurung kurawal tambahan:
using var writer = new StreamWriter(path); // C# 8: disposed when the method returns
writer.WriteLine("INFO server started");
Path dan folder
Susun path dengan Path.Combine, bukan dengan menggabungkan string. Method ini menyisipkan pemisah yang tepat untuk sistem operasinya (\ di Windows, / di Linux dan macOS) dan tidak menggandakannya. Class Path juga bisa memecah path, dan Directory membuat dan menampilkan isi folder:
Output:
february.txt, january.txt, march.txt
march.txt
march
.txt
march.pdf
False
Satu jebakan: kalau argumen yang lebih belakang di Path.Combine berupa path absolut (diawali / atau \, atau huruf drive di Windows), semua yang ada sebelumnya dibuang, jadi Path.Combine("reports", "/2026") menghasilkan /2026. Berikan bagian yang relatif, atau pakai Path.Join (.NET Core 3.0 ke atas), yang tidak pernah membuang apa pun.
Directory.GetFiles mengembalikan file sesuai urutan yang diberikan file system, yang berbeda antara Windows dan Linux, jadi urutkan hasilnya saat urutan itu penting. Directory.EnumerateFiles adalah versi lazy-nya, dengan hubungan yang sama seperti ReadLines terhadap ReadAllLines. Berikan SearchOption.AllDirectories untuk menyertakan subfolder.
Menangani error
Operasi file gagal karena alasan di luar programmu: file-nya tidak ada, folder-nya tidak ada, proses lain sedang membukanya, disk penuh, izinnya salah. Masing-masing punya tipe exception sendiri. FileNotFoundException dan DirectoryNotFoundException diturunkan dari IOException, jadi tangkap keduanya lebih dulu; UnauthorizedAccessException tidak, jadi butuh catch sendiri:
Output:
missing file, using defaults
missing folder, using defaults
theme=dark
File.Exists sebelum membaca terlihat lebih sederhana, tetapi tidak menghilangkan kebutuhan akan try: file bisa hilang, atau dikunci program lain, di antara pengecekan dan pembacaan. Pakai Exists untuk memutuskan apa yang dilakukan, dan catch untuk bertahan dari apa yang benar-benar terjadi.
Encoding
File.WriteAllText, WriteAllLines, dan StreamWriter secara default menulis UTF-8 tanpa byte order mark, dan pembacanya mendeteksi UTF-8, UTF-16, dan UTF-32 dari byte order mark kalau ada. Berikan sebuah Encoding saat file harus dalam format tertentu:
Output:
plain.txt 16 bytes, reads back unchanged: True
bom.txt 19 bytes, reads back unchanged: True
utf16.txt 30 bytes, reads back unchanged: True
é dan ã masing-masing memakai dua byte di UTF-8, itulah sebabnya 14 karakter butuh 16 byte, dan ketiga file terbaca kembali dengan benar karena pembacanya mengenali byte order mark. Encoding.UTF8 terlihat seperti default, tetapi menambahkan byte order mark, yang oleh sebagian tool (importer CSV lama, shell script) ditampilkan sebagai karakter aneh di awal baris pertama; pakai new UTF8Encoding(false) untuk UTF-8 eksplisit tanpa byte order mark. Membaca file dengan encoding yang salah tidak melempar exception: hasilnya berupa karakter pengganti, jadi cari tahu encoding apa yang dipakai file yang bukan buatanmu.
I/O file async
Di aplikasi web dan kode UI, memblokir thread untuk I/O disk berarti membuang thread itu. .NET Core 2.0 ke atas punya versi async dari method File:
await File.WriteAllTextAsync("notes.txt", text);
string content = await File.ReadAllTextAsync("notes.txt");
string[] lines = await File.ReadAllLinesAsync("sales.csv");
StreamReader.ReadLineAsync dan StreamWriter.WriteLineAsync bekerja dengan cara yang sama pada stream.
Kesalahan umum
- Tidak men-dispose stream. Tanpa
using, teks di buffer mungkin tidak pernah sampai ke disk dan file-nya tetap terkunci sampai garbage collector berjalan. - Menyusun path dengan
+ "\\" +. Cara ini rusak di Linux dan macOS. PakaiPath.Combine. - Memuat file sangat besar dengan
ReadAllTextatauReadAllLines. Baca secara bertahap denganReadLinesatauStreamReader. - Mengandalkan direktori kerja saat ini. Direktori ini bergantung pada cara program dijalankan. Pakai path absolut atau
AppContext.BaseDirectory. - Mem-parse angka dari file dengan culture mesin. Berikan
CultureInfo.InvariantCultureuntuk file data. - Mengira
File.Existsmencegah exception. File-nya bisa berubah di antara pengecekan dan pemakaian.
Pertanyaan yang sering diajukan
Bagaimana cara menulis teks ke file di C#?
File.WriteAllText("notes.txt", text); membuat file-nya, atau menimpanya kalau sudah ada, menulis string-nya sebagai UTF-8, lalu menutupnya. Pakai File.WriteAllLines(path, lines) untuk kumpulan baris dan File.AppendAllText(path, text) untuk menambahkan ke bagian akhir alih-alih mengganti isinya. Ketiganya ada di System.IO.
Bagaimana cara membaca file teks di C#?
string text = File.ReadAllText("notes.txt"); membaca seluruh file menjadi satu string, dan string[] lines = File.ReadAllLines(path); memecahnya menjadi baris-baris. Untuk file besar, pakai File.ReadLines(path) di dalam foreach, yang membaca satu baris setiap kali alih-alih memuat semuanya, atau sebuah StreamReader.
Bagaimana cara menambahkan teks ke file di C#?
File.AppendAllText(path, text) menambahkan teks ke akhir file dan membuat file-nya kalau belum ada; File.AppendAllLines(path, lines) melakukan hal yang sama untuk baris. Dengan stream, buka dalam mode append: new StreamWriter(path, append: true). Ingat untuk menambahkan Environment.NewLine atau \n sendiri saat menambahkan teks.
Bagaimana cara membaca file baris per baris di C#?
foreach (string line in File.ReadLines(path)) { ... } membaca file satu baris setiap kali, jadi pemakaian memori tetap rendah bahkan untuk log berukuran gigabyte. Padanan lamanya adalah StreamReader di dalam blok using dengan while ((line = reader.ReadLine()) != null). Hindari ReadAllLines untuk file besar: method ini memuat setiap baris ke dalam array lebih dulu.
Bagaimana cara mengecek apakah file ada di C#?
File.Exists(path) mengembalikan true kalau file-nya ada dan bisa dilihat program, dan Directory.Exists(path) melakukan hal yang sama untuk folder. Jangan mengandalkannya untuk menghindari exception: file bisa dihapus atau dikunci di antara pengecekan dan pembacaan, jadi tetap tangani FileNotFoundException dan IOException di sekitar operasi file yang sebenarnya.