Eğitim Portalı/Java/Dosya İşlemleri ve I/O
Java01-java/14-dosya-ve-io

Dosya İşlemleri ve I/O

Şimdiye kadar veriler programın içinde, bellekte yaşadı. Ama gerçek uygulamalar dış dünyayla konuşur: dosya okur/yazar, ağdan veri alır, kayıt tutar. Bu bölümde Java'da girdi/çıktı (I/O) işlemlerini, hem klasik akış (…

Dosya İşlemleri ve I/O

Şimdiye kadar veriler programın içinde, bellekte yaşadı. Ama gerçek uygulamalar dış dünyayla konuşur: dosya okur/yazar, ağdan veri alır, kayıt tutar. Bu bölümde Java'da girdi/çıktı (I/O) işlemlerini, hem klasik akış (stream) yaklaşımını hem de modern ve çok daha kısa olan NIO.2 API'sini öğreniyoruz.

Bu bölümün örnekleri repo klasörünü kirletmemek için geçici dosyalar (Files.createTempFile) kullanır ve iş bitince onları siler. Kendi projelerinde elbette kalıcı yollar kullanırsın.

Klasik I/O: akışlar (streams)

Java'nın orijinal I/O modeli akışlar üzerine kuruludur: veriyi byte byte veya karakter karakter okuyup yazarsın. Verimlilik için bunları tamponlu (buffered) sarmalayıcılarla kullanırız:

  • Yazma: FileWriterBufferedWriter (write, newLine)
  • Okuma: FileReaderBufferedReader (readLine)
try (BufferedWriter w = new BufferedWriter(new FileWriter(dosya))) {
    w.write("satır");
    w.newLine();
}
try (BufferedReader r = new BufferedReader(new FileReader(dosya))) {
    String satir;
    while ((satir = r.readLine()) != null) { ... }
}

Dikkat: akışlar birer kaynaktır ve mutlaka kapatılmalıdır. try-with-resources bunu otomatik yapar (blok bitince close() + tampon boşaltma). Örnek 1 (./Ornek1.java) tamponlu yazma ve satır satır okumayı gösterir.

Modern I/O: NIO.2 (Path ve Files)

Java 7 ile gelen java.nio.file paketi, sık yapılan işleri tek satıra indirir. Path bir dosya yolunu temsil eder; Files ise yüksek seviyeli yardımcı metotlar sunar:

Files.write(path, List.of("a", "b", "c"));   // listeyi satır satır yaz
List<String> satirlar = Files.readAllLines(path); // tümünü oku
Files.writeString(path, "ek\n", StandardOpenOption.APPEND); // sona ekle
Files.exists(path); Files.size(path); Files.delete(path);

Path ayrıca yol parçalarıyla çalışmayı kolaylaştırır (getFileName, getParent). Örnek 2 (./Ornek2.java) NIO ile yazma/okuma/ekleme ve dosya bilgisi almayı gösterir.

Küçük dosyalarda readAllLines pratiktir; çok büyük dosyalarda ise tümünü belleğe almak yerine satır satır akıtan Files.lines(...) tercih edilir.

Files.lines + Stream

Files.lines(path) bir Stream<String> döndürür; böylece dosyayı satır satır, tembel biçimde ve Stream API'nin tüm gücüyle işleyebilirsin. Bu stream bir kaynak tuttuğundan try-with-resources içinde kullanılmalıdır:

try (Stream<String> satirlar = Files.lines(path)) {
    satirlar.filter(s -> s.startsWith("ERROR")).forEach(System.out::println);
}

Örnek 3 (./Ornek3.java) küçük bir log dosyası oluşturur, seviyelere (INFO/WARN/ERROR) göre kaç kayıt olduğunu groupingBy ile sayar ve yalnızca hata satırlarını süzer — dosya işleme ile Stream API'yi bir araya getirir.

byte vs karakter akışları

İki tür akış vardır: byte akışları (InputStream/OutputStream) her tür ikili veri (resim, ses, zip) için; karakter akışları (Reader/Writer) metin için kullanılır. Metinle çalışırken karakter akışlarını (veya NIO'nun metin metotlarını) tercih edersin; böylece karakter kodlaması (UTF-8 gibi) doğru ele alınır.

Özet

Klasik tamponlu akışlarla dosya okuyup yazmayı, modern NIO.2 (Path/Files) ile aynı işleri çok daha kısa yapmayı ve Files.lines ile dosyaları Stream halinde işlemeyi öğrendik. try-with-resources ile kaynakları güvenle kapattık. Sırada, programları aynı anda birden çok iş yapabilir hale getiren konu: çoklu iş parçacığı ve eşzamanlılık.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Klasik I/O — BufferedWriter ile yazma, BufferedReader ile okuma.
2// Çalıştırma: java Ornek1.java
3import java.io.BufferedReader;
4import java.io.BufferedWriter;
5import java.io.FileReader;
6import java.io.FileWriter;
7import java.io.IOException;
8import java.nio.file.Files;
9import java.nio.file.Path;
10
11public class Ornek1 {
12
13    public static void main(String[] args) throws IOException {
14        // Geçici dosya kullanıyoruz; repo klasörünü kirletmemek için.
15        Path dosya = Files.createTempFile("ornek-io-", ".txt");
16        System.out.println("Geçici dosya: " + dosya);
17
18        // --- Yazma ---
19        // try-with-resources: akış blok bitince otomatik kapanır (flush + close).
20        try (BufferedWriter yazici = new BufferedWriter(new FileWriter(dosya.toFile()))) {
21            yazici.write("Birinci satır");
22            yazici.newLine();
23            yazici.write("İkinci satır");
24            yazici.newLine();
25            yazici.write("Üçüncü satır");
26            yazici.newLine();
27        }
28        System.out.println("Dosyaya 3 satır yazıldı.");
29
30        // --- Okuma ---
31        System.out.println("\nDosya içeriği:");
32        int satirNo = 0;
33        try (BufferedReader okuyucu = new BufferedReader(new FileReader(dosya.toFile()))) {
34            String satir;
35            while ((satir = okuyucu.readLine()) != null) { // satır kalmayınca null döner
36                satirNo++;
37                System.out.println("  " + satirNo + ": " + satir);
38            }
39        }
40
41        // Temizlik
42        Files.deleteIfExists(dosya);
43        System.out.println("\nGeçici dosya silindi.");
44    }
45}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Modern NIO.2 — Path ve Files ile kısa dosya işlemleri.
2// Çalıştırma: java Ornek2.java
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6import java.util.List;
7
8public class Ornek2 {
9
10    public static void main(String[] args) throws IOException {
11        Path dosya = Files.createTempFile("ornek-nio-", ".txt");
12        System.out.println("Geçici dosya: " + dosya.getFileName());
13
14        // NIO ile yazma: tek satırda tüm listeyi yaz.
15        List<String> satirlar = List.of("elma", "armut", "kiraz", "muz");
16        Files.write(dosya, satirlar);
17        System.out.println("Yazılan satır sayısı: " + satirlar.size());
18
19        // NIO ile okuma: tüm satırları tek seferde listeye al.
20        List<String> okunan = Files.readAllLines(dosya);
21        System.out.println("Okunan: " + okunan);
22
23        // Sona ekleme (append)
24        Files.writeString(dosya, "çilek\n", java.nio.file.StandardOpenOption.APPEND);
25        System.out.println("Ekleme sonrası satır sayısı: " + Files.readAllLines(dosya).size());
26
27        // Dosya bilgileri
28        System.out.println("\nVar mı?    : " + Files.exists(dosya));
29        System.out.println("Boyut (byte): " + Files.size(dosya));
30
31        // Path ile yol işlemleri (dosya oluşturmadan, sadece metin üzerinde)
32        Path ornekYol = Path.of("/home/kullanici/belgeler/rapor.pdf");
33        System.out.println("\nPath örneği: " + ornekYol);
34        System.out.println("  dosya adı : " + ornekYol.getFileName());
35        System.out.println("  üst dizin : " + ornekYol.getParent());
36
37        Files.deleteIfExists(dosya);
38        System.out.println("\nGeçici dosya silindi.");
39    }
40}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Log dosyası yazma ve Files.lines + Stream ile analiz.
2// Çalıştırma: java Ornek3.java
3import java.io.IOException;
4import java.nio.file.Files;
5import java.nio.file.Path;
6import java.util.List;
7import java.util.Map;
8import java.util.stream.Collectors;
9import java.util.stream.Stream;
10
11public class Ornek3 {
12
13    public static void main(String[] args) throws IOException {
14        Path log = Files.createTempFile("ornek-log-", ".log");
15
16        // Basit bir log dosyası oluştur (seviye + mesaj).
17        List<String> kayitlar = List.of(
18                "INFO  uygulama başladı",
19                "WARN  bellek %80",
20                "INFO  istek alındı",
21                "ERROR veritabanı bağlantısı koptu",
22                "INFO  istek tamamlandı",
23                "ERROR zaman aşımı",
24                "WARN  yavaş sorgu"
25        );
26        Files.write(log, kayitlar);
27        System.out.println("Log dosyası yazıldı (" + kayitlar.size() + " kayıt).\n");
28
29        // Files.lines bir Stream<String> döndürür; try-with-resources ile kapatılmalı.
30        // Seviyelere göre kaç kayıt var?
31        try (Stream<String> satirlar = Files.lines(log)) {
32            Map<String, Long> seviyeSayisi = satirlar
33                    .map(s -> s.split("\\s+")[0])      // ilk kelime = seviye
34                    .collect(Collectors.groupingBy(s -> s, Collectors.counting()));
35            System.out.println("Seviye dağılımı: " + seviyeSayisi);
36        }
37
38        // Sadece ERROR satırlarını süz.
39        try (Stream<String> satirlar = Files.lines(log)) {
40            System.out.println("\nHata kayıtları:");
41            satirlar.filter(s -> s.startsWith("ERROR"))
42                    .forEach(s -> System.out.println("  " + s));
43        }
44
45        Files.deleteIfExists(log);
46        System.out.println("\nGeçici log dosyası silindi.");
47    }
48}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.