İçindekilerGirişİndex
YukarıİlkÖnceki YokSonrakiSon
Geriİleri
Yazdır

Stream'ler

Input ve Output Stream'leri

I/O (Input/Output) bilgisayarın hafızası kadar hızlı olmayan bir birimde belli bir verinin okunması ve yazılması işlemidir. Örnek olarak bir disk üzerindeki dosyalar ele alınabilir. Disk üzerindeki dosyalara erişim hafızadaki bilgilere erişim kadar kolay olmaz. Bir dosyadan okuma veya dosyaya yazma yavaş yavaş (hatta bazen byte byte) yapılır. Ayrıca network bağlantıları da aynı şekilde değerlendirilmelidir. Zira network üzerinden başka bir makineyle okuma/yazma yapmak elbette yavaş yavaş gerçekleştirilir. Bazen okunacak veri hazır olmayabilir veya yazılacak birim henüz yazılan datayı aynı hızda alacak durumda olmayabilir. Böyle durumlarda Stream kavramı gündeme gelir. Okumak için InputStream ve yazmak için de OutputStream kullanılır. Streamlar 'byte' konuşurlar. Bir dosya okumak için, FileInputStream ve bir dosyaya yazmak için FileOutputStream kullanılır.

InputStream'den Okuma

Bir dosyadan byte byte okuma yapıp ekrana basan bir program yazalım.

Göster Gizle Kopar Satır Gizle Satır Göster
  1 import java.io.*;
  2 public class InputStreamer{
  3   public static void main(String[] args)
  4     throws IOException
  5   {
  6     String filename="c:\\myfolder\\myfile.ext";
  7     FileInputStream in=new FileInputStream(filename);
  8     int c;
  9     while ((c = in.read()) != -1){
 10          System.out.print(c);  
 11     }
 12     in.close();
 13   }
 14 }

InputStream'in read() methodu bir int okur ancak bu aslında bir byte'tır. Okuma yapmayı bitirince -1 döndürür. Burdan artık stream'den gelecek veri olmadığını anlarız.

OutputStream'e Yazma.

Elimizdeki bir karakter dizisini bir dosyaya byte byte yazan bir program yapalım.

Göster Gizle Kopar Satır Gizle Satır Göster
  1 import java.io.*;
  2 public class OutputStreamer{
  3   public static void main(String[] args)
  4     throws IOException
  5   {
  6     String filename="c:\\myfolder\\myfile.ext";
  7     FileOutputStream out=new FileOutputStream(filename);
  8     char[] cs=new char[]{'J','a','v','a'};  
  9     for(int i=0;i<cs.length;i++){
 10       char c=cs[i];
 11       out.write(c);
 12     }
 13     out.close();
 14   }
 15 }

OutputStream'in write() methodu veriyi yazmak için kullanılır.

File olmayan Stream'ler

Bİr file'dan okumak için niye hem InputStream hem de FileInputStream'a ihtiyaç duyulduğu sorusu akla gelebilir. InputStream genel bir kaynaktan byte byte okumaya yarar. FileInputStream'se file'dan okuma yapmak içindir. Aslında FileInputStream, InputStream'i extend eder, yani onun özel bir halidir. FileInputStream dışında network'ten okumak için veya bir ses cihazından okumak için, veya herhangi bir cihazdan okumak için kullanılan InputStream'ler bulunur. Aslında

    FileInputStream in=new FileInputStream(filename);

yerine

    InputStream in=new FileInputStream(filename);

da yazılabilirdi. Okuma kodu değişmezdi. Aynı şekilde AudioInputStream'la

    InputStream in=new AudioInputStream(...);

gibi bir kod yazarak ses cihazından veya dosyasından okuma yapabiliriz. Kod yine değişmez. Bütün bunlar OutputStream'ler için de söylenebilir. Java'nın I/O kütüphanesindeki karmaşıklığın sebeplerinden biri stream'lerin genel bir çözüm içermesi, sadece dosyalarla sınırlandırılmamış olmasıdır.

Rasgele Erişimli Dosyalar

Hem okuma hem yazma yapma ihtiyacı olduğunda kullanılabilecek bir RandomAccessFile adında bir class vardır. Ancak normal şartlarda aynı anda hem okuma hem yazma gerekmez. Bu class bunun gerekli olduğu nadir durumlarda kullanılır. Hem input stream'lerde olan read methodları hem de output stream'lerde olan write method'ları vardır.

Buffer'lı Stream'ler

Input ve output stream'ler byte byte okuma yaptıkları için oldukça yavaş çalışırlar. Örneğin diskten bir dosya okunacaksa, bir byte okumak için bile, manyetik ve mekanik esaslarla çalışan disk cihazının okuma yapan kafası diskin ilgili bülümüne yerleştirilecek ve sonra okuma yapılacaktır. Aynı yerden 1000 byte'lık bir veri okunsa da aynı işlem 1000 kere tekrarlanacaktır. Disklerde bu durumu hafifletecek bazı özellikler mevcuttur ancak yazılımda da yapılması gereken bazı işlemler vardır. Burada 'buffering' (tamponlama) devreye girer. Okuma yaparken bir byte bile okunacaksa belli bir miktar (örneğin 1024 tane) byte hafızaya, yani bir buffer' (tampon)'a okunur. İkinci byte okunurken diskten değil buffer'dan okunur. Buffer'daki sayı tükenene kadar böyle gider. Ne zaman ki artık bufferda hiç bir şey kalmaz, gidilir tekrar diskten okuma yapılır. Tabii yine bir byte değil buffer size'ı kadar.

Yazma yaparken de buffer kullanılabilir. Belli bir size'da buffer açılır ve stream'a yazılan değerler diske değil bu buffer'a yazılır. Ne zaman ki buffer size'ı dolar, bütün buffer içeriği diske tek seferde yazılır. Bu durumda disk açısından tek yazma işlemi gerçekleştiğinden sistem hızlı çalışır. Ancak yazma yaparken bir sorun var : Ya buffer dolup diske yazma yapmadan programa bir şey olur da işlem yarım kalırsa? Çünkü buffer'da diske yazılmayı bekleyen byte'lar diskteki dosyada yok. Veri eksik kalmış oluyor. O yüzden, öncelikle buffer size'ı küçük tutlmalıdır. Ayrıca 'flush' diye bir işlem vardır. Bu işlem 'buffer'daki veriyi hemen diske yaz, buffer dolmuş olsun olmasın' demektir. Programcı belli bir anlam bütünlüğü olan veriyi yazdıktan sonra flush() method'unu çağırarak buffer'ı diske yazar.

Programcı bu buffer işlemlerini kendi yapmak zorunda değildir. Bu işler çin BufferedInputStream ve BufferedOutputStream vardır. Bunlar bir stream'le disk arasına buffer koyarlar. Buffer'ın size'ı istenirse verilir verilmezse default bir değerde olur. Buffer kullanmak basittir :

InputStream is=...;
BufferedInputStream bis=new BufferedInputStream(is);

Bundan sonraki kodda InputStream yerine BufferedInputStream kullanılır. BufferedInputStream'de aslında bir InputStream'dir ve aynı methodlra sahiptir, o yüzden aynı şekilde kullanılır. Aynı şeyler OutputStream ve BufferedOutputStream için geçerlidir.

'Madem buffer bu kadar faydalı niye her stream buffer'lı yapılmıyor. Böylelikle iki ayrı class'dan da kurtulmuş olurduk' diye düşünülebilir. Ancak bazı stream'ler buffer'sız olmalıdır. Gelen byte'ın anında okunması veya yazılması gerekebilir. Bu durumda buffer veriyi arada beklettiği için zararlı olabilir.

Stream'leri Kapama

Stream'ler işlem yapmadan önce 'açılır' ve işlem yapıldıktan sonra 'kapanır'. Java'da açma işlemi stram'lerin constructor'larında otomatik olarak yapılır. Ancak kapamak için 'close()' kullanılmalıdır. Programdan çıkılırken stream'ler açık dahi olsa kapatılabilir. O yüzden sadece main'den oluşan programlarda bu sorun anlaşılmaz. Ancak buffer'lı stream'lerde close() şarttır. Çünkü buffer'da kalan verilerle ilgili işlemler, gerekiyorsa flush da close'da yapılır.

Dosya Listesi

İçindekilerGirişİndex
YukarıİlkÖnceki YokSonrakiSon
Geriİleri
Yazdır