System.IO namespace'i dosyalarla çalışmak için her şeye sahiptir. Çoğu görev için statik File sınıfındaki tek bir çağrı yeterlidir: dosyayı açar, okur ya da yazar ve kapatır. Büyük dosyalar ya da ince kontrol için StreamReader ve StreamWriter kullanın. Aşağıdaki her örneğe using System.IO; ekleyin.
Tüm bir dosyayı yazmak ve okumak
File.WriteAllText verdiğiniz metinle bir dosya oluşturur (ya da mevcut birinin üzerine yazar). File.ReadAllText tüm dosyayı tek bir string olarak döndürür. File.AppendAllText sona ekler:
Çıktı:
True
Buy coffee
Call the dentist
Renew passport
43 characters
Start over
False
"notes.txt" gibi göreli bir yol sürecin geçerli klasörüne göre çözülür; bu her zaman programınızın bulunduğu klasör değildir (bir servis, zamanlanmış bir görev ya da bir test çalıştırıcısı başka bir yerde başlayabilir). Uygulamanızla birlikte gelen dosyalar için yolu AppContext.BaseDirectory'den oluşturun.
WriteAllText sormadan üzerine yazar. Eski içeriği kaybetmek önemliyse önce File.Exists'i kontrol edin ya da geçici bir dosyaya yazıp sonra onu File.Move ile yerine taşıyın.
Satırlar: WriteAllLines, ReadAllLines ve ReadLines
Satır başına bir kayıt içeren dosyalar kendi metotlarına sahip olacak kadar yaygındır. WriteAllLines her string'i ardından bir satır sonuyla yazar; ReadAllLines bir string[] döndürür:
Çıktı:
4 lines, header: date,product,amount
Total: 45.60
2026-03-03,Stapler,7.00
İki okuyucu arasındaki fark boyut için önemlidir. ReadAllLines dönmeden önce tüm dosyayı belleğe okur. ReadLines ise siz dolaştıkça okuyan tembel bir IEnumerable<string> döndürür, bu yüzden 5 GB'lık bir log üzerindeki bir foreach birkaç kilobayt kullanır. LINQ ile ReadLines(...).Where(...).Take(10) on eşleşme bulduktan sonra okumayı bırakır.
CSV'yi Split(',') ile parse etmek kendi ürettiğiniz basit dosyalar için çalışır. Gerçek dünyadaki CSV'lerde virgül içeren tırnaklı alanlar vardır; bunlar için CsvHelper gibi bir kütüphane kullanın.
decimal.Parse içindeki InvariantCulture'a dikkat edin: o olmadan Almanca ya da Portekizceye ayarlı bir makinede "4.50"'yi parse etmek noktayı binlik ayırıcı olarak okur.
StreamWriter ve StreamReader
File metotları dosyayı her çağrıda açıp kapatır. Zamanla birçok parça yazmak ya da LINQ olmadan belleğe sığmayacak kadar büyük bir dosyayı okumak için bir stream'i bir kez açın. Bir istisna fırlatılsa bile kapanması için onu her zaman using içine alın; kapatılmamış bir yazıcı tamponunu hiç boşaltmayabilir ve dosya boş ya da kesik kalır.
Çıktı:
2: WARN disk 85% full
4: ERROR connection refused
ReadLine dosyanın sonunda null döndürür; while döngüsünü bitiren budur. Bir StreamWriter çıktısını tamponlar ve tampon dolduğunda, Flush() çağırdığınızda ve dispose edildiğinde diske yazar; using bloğu sonuncusunu garanti eder.
C# 8'den beri bir using bildirimi, ek süslü parantezler olmadan stream'i çevreleyen bloğun sonunda dispose eder:
using var writer = new StreamWriter(path); // C# 8: disposed when the method returns
writer.WriteLine("INFO server started");
Yollar ve klasörler
Yolları string birleştirmek yerine Path.Combine ile oluşturun. İşletim sistemi için doğru ayırıcıyı ekler (Windows'ta \, Linux ve macOS'ta /) ve onu ikilemez. Path sınıfı yolları parçalarına da ayırır ve Directory klasörleri oluşturur ve listeler:
Çıktı:
february.txt, january.txt, march.txt
march.txt
march
.txt
march.pdf
False
Bir tuzak: Path.Combine'a sonradan verilen bir argüman köklüyse (/ ya da \ ile, Windows'ta bir sürücü harfiyle başlıyorsa) ondan önceki her şey atılır, yani Path.Combine("reports", "/2026") /2026'dır. Göreli parçalar verin ya da hiçbir şeyi atmayan Path.Join'i (.NET Core 3.0 ve sonrası) kullanın.
Directory.GetFiles dosyaları dosya sisteminin verdiği sırayla döndürür ve bu sıra Windows ile Linux arasında farklıdır, bu yüzden sıra önemliyse sonucu sıralayın. Directory.EnumerateFiles tembel sürümdür; ReadLines ile ReadAllLines arasındaki ilişkinin aynısı. Alt klasörleri dahil etmek için SearchOption.AllDirectories verin.
Hataları ele almak
Dosya işlemleri programınızın dışındaki nedenlerle başarısız olur: dosya eksiktir, klasör yoktur, başka bir süreç onu açık tutuyordur, disk doludur, izinler yanlıştır. Her birinin kendi istisna tipi vardır. FileNotFoundException ve DirectoryNotFoundException IOException'dan türer, bu yüzden onları ondan önce yakalayın; UnauthorizedAccessException ondan türemez, bu yüzden kendi catch'ine ihtiyaç duyar:
Çıktı:
missing file, using defaults
missing folder, using defaults
theme=dark
Bir okumadan önce File.Exists daha basit görünür, ama try ihtiyacını ortadan kaldırmaz: dosya kontrol ile okuma arasındaki anda kaybolabilir ya da başka bir program tarafından kilitlenebilir. Ne yapacağınıza karar vermek için Exists'i, gerçekte olana dayanmak için catch'i kullanın.
Kodlama
File.WriteAllText, WriteAllLines ve StreamWriter varsayılan olarak byte order mark olmadan UTF-8 yazar ve okuyucular, varsa bir byte order mark'tan UTF-8, UTF-16 ve UTF-32'yi algılar. Bir dosyanın belirli bir biçimde olması gerektiğinde bir Encoding verin:
Çıktı:
plain.txt 16 bytes, reads back unchanged: True
bom.txt 19 bytes, reads back unchanged: True
utf16.txt 30 bytes, reads back unchanged: True
é ve ã UTF-8'de her biri iki bayt kaplar; 14 karakterin 16 bayta ihtiyaç duymasının nedeni budur ve okuyucu byte order mark'ı tanıdığı için üç dosya da doğru okunur. Encoding.UTF8 varsayılan gibi görünür ama bazı araçların (eski CSV içe aktarıcılar, shell betikleri) ilk satırın başında çöp olarak gösterdiği bir byte order mark ekler; onsuz açık UTF-8 için new UTF8Encoding(false) kullanın. Bir dosyayı yanlış kodlamayla okumak istisna fırlatmaz: yerine geçme karakterleri üretir, bu yüzden oluşturmadığınız bir dosyanın hangi kodlamayı kullandığını öğrenin.
Async dosya G/Ç
Web uygulamalarında ve UI kodunda bir thread'i disk G/Ç'sinde bloklamak onu boşa harcar. .NET Core 2.0 ve sonrasında File metotlarının async sürümleri vardır:
await File.WriteAllTextAsync("notes.txt", text);
string content = await File.ReadAllTextAsync("notes.txt");
string[] lines = await File.ReadAllLinesAsync("sales.csv");
StreamReader.ReadLineAsync ve StreamWriter.WriteLineAsync stream'lerde aynı şekilde çalışır.
Yaygın hatalar
- Bir stream'i dispose etmemek.
usingolmadan tamponlanmış metin diske hiç ulaşmayabilir ve dosya çöp toplayıcı çalışana kadar kilitli kalır. - Yolları
+ "\\" +ile oluşturmak. Linux ve macOS'ta bozulur.Path.Combinekullanın. - Büyük dosyaları
ReadAllTextya daReadAllLinesile yüklemek. OnlarıReadLinesya da birStreamReaderile akıtın. - Geçerli klasöre güvenmek. Programın nasıl başlatıldığına bağlıdır. Mutlak yollar ya da
AppContext.BaseDirectorykullanın. - Dosyalardaki sayıları makinenin kültürüyle parse etmek. Veri dosyaları için
CultureInfo.InvariantCultureverin. File.Exists'in istisnaları önlemesini beklemek. Dosya kontrol ile kullanım arasında değişebilir.
Sıkça Sorulan Sorular
C#'ta bir dosyaya nasıl metin yazılır?
File.WriteAllText("notes.txt", text); dosyayı oluşturur ya da varsa üzerine yazar, string'i UTF-8 olarak yazar ve kapatır. Bir satır koleksiyonu için File.WriteAllLines(path, lines), değiştirmek yerine sona eklemek için File.AppendAllText(path, text) kullanın. Üçü de System.IO içinde yaşar.
C#'ta bir metin dosyası nasıl okunur?
string text = File.ReadAllText("notes.txt"); tüm dosyayı tek bir string'e okur, string[] lines = File.ReadAllLines(path); ise onu satırlara böler. Büyük dosyalar için her şeyi yüklemek yerine her seferinde bir satır okuyan foreach içinde File.ReadLines(path) ya da bir StreamReader kullanın.
C#'ta bir dosyaya nasıl ekleme yapılır?
File.AppendAllText(path, text) dosyanın sonuna metin ekler ve dosya yoksa oluşturur; File.AppendAllLines(path, lines) satırlar için aynısını yapar. Bir stream ile onu ekleme modunda açın: new StreamWriter(path, append: true). Metin eklerken Environment.NewLine'ı ya da \n'yi kendiniz eklemeyi unutmayın.
C#'ta bir dosya satır satır nasıl okunur?
foreach (string line in File.ReadLines(path)) { ... } dosyayı her seferinde bir satır akıtır, bu yüzden gigabaytlık log'larda bile bellek kullanımı sabit kalır. Daha eski karşılığı, while ((line = reader.ReadLine()) != null) ile bir using bloğundaki StreamReader'dır. Büyük dosyalar için ReadAllLines'tan kaçının: önce her satırı bir diziye yükler.
C#'ta bir dosyanın var olup olmadığı nasıl kontrol edilir?
File.Exists(path) dosya varsa ve program onu görebiliyorsa true döndürür, Directory.Exists(path) klasörler için aynısını yapar. İstisnalardan kaçınmak için ona güvenmeyin: dosya kontrol ile okuma arasında silinebilir ya da kilitlenebilir, bu yüzden asıl dosya işleminin etrafında yine FileNotFoundException ve IOException'ı ele alın.