Makale Özeti

Bir önceki yazıda göz attığımız kavramlara daha yakından ve örneklerle bakıyoruz

Makale


C# ile Dosya işlemleri (I/O) -2-
Bir önceki yazıda basit olarak dosya ve dizin işlemlerini, Directory, DirectoryInfo, File, FileInfo sınıflarını inceledik. Dosya okuma ve dosyaya yazma işlemlerinde kullanacağımız sınıfların bir listesine göz attık. Artık dosya işlemlerine geçebiliriz.
Binary Dosya İşlemleri
Bu bölümde binary dosyalardan okuma ve yazma işlemlerini inceleyeceğiz. Binary dosyadan okuma (binary read) işleminin text dosya okumadan (text read) farkını göreceğiz. Bir dosyanın hangi tür dosya olduğunu bilmiyorsanız en güvenli yol o dosyayı binary olarak işleme sokmaktır. Bir binary dosyayla işlem yapmak istiyorsak, bir tane okumak için bir tanede yazmak için Stream nesnesi oluştururuz.
Stream inputStream =File.OpenRead(@”c:\deneme\source\test1.cs”);
Stream outputStream =File.OpenRead(@”c:\deneme\source\test1.bak”);
Örnekte de görüldüğü gibi bir dosyayı okuma yada yazma amaçlı açmak için File sınıfının OpenRead() ve OpenWrite() metotları kullanılır. Bu metotlar argüman olarak dosyanın yolunu (path) alır.
Binary dosyadan okunan veri bir bufferda tutulur. Buffer, Read() metodunun okuduğu verileri tutacak olan bir dizidir.
InputStream.Read() ‘e buffer başlangıç konumu (offset) ve buffera okunacak verinin boyutunu girerek veriyi buffera okuyabilir ve dönen değerlede ne kadar verinin okunduğu bilgisini alabilirsiniz.
while(okunanveriboyutu=(inputStream.Read(buffer, 0, Buffer_Boyut))>0)
{
outputStream.Write(buffer,o,okunanveriboyutu)
}
Buffera veri yazıldığı müddetçe dosyadan okunan her byte yeni dosyaya yazılacaktır. Write komutunun argümanları; verinin okunacağı buffer, okumaya başlanacak pozisyon ve ne kadar veri okunacağıdır.
Tam bir örnek üzerinde bilgilerimizi pekiştirelim.
using System;
using System.IO;
namespace princeNET
{
 class Binary
 {
  const int BufferBoyut=1024;
  public static void Main(string[] args)
  {
  Binary b=new Binary();
  b.Run();
  }
  private void Run()
  {
  Stream inputStream=File.OpenRead(@"c:\deneme\source\binary.cs");
  Stream outputStream=File.OpenWrite(@"C:\deneme\source\binary.bak");
  //şimdi buffer oluşturalım
  byte[] buffer = new Byte[BufferBoyut];
  int kacbyte;
  //read metodu dosyadan okuduğu müddetçe herbir byteı dosyaya yaz
  while((kacbyte=inputStream.Read(buffer,0,BufferBoyut))>0)
  {
  outputStream.Write(buffer,0,BufferBoyut);
  }
  inputStream.Close();
  outputStream.Close();
  }
 }
}
Buffered Stream
Yukarıdaki örnekte bir buffera istediğimiz büyüklükte veri okuyup bu veriyi daha sonra başka bir dosyaya yazma işlemini gördük. Fakat buffered stream nesnesi ile buffera okunacak verinin veya yazılırken ne kadarının yazılmasının daha verimli olacağının otomatik olarak ayarlanmasını sağlayabiliriz. Sonuçta okuma ve yazma işlemi en hızlı şekilde gerçekleştirilir.
BufferedStream nesnesi mevcut Stream nesnesinden oluşturulur. İlk önce yukarıda yaptığımız gimi stream nesnesi;
Stream inputStream=File.OpenRead(@”c:\deneme\source\binary2.cs”);
Stream outputStream=File.OpenWrite(@“c:\deneme\source\binary2.bak”);
oluşturulduktan sonra bu stream nesnesini bufferedstream constructura paramatre olarak geçirip bufferedstream nesnesini oluştururuz.
BufferedStream bufferedInput=new BufferedStream(inputStream);
BufferedStream bufferedOutput=new BufferedOutput(outputStream);
Okuma ve yazma işlemini aynen yukarıda yaptığımız gibi yapacağız.
while(okunanveriboyutu=(inputStream.Read(buffer, 0, Buffer_Boyut))>0)
{
outputStream.Write(buffer,o,okunanveriboyutu)
}
bufferedOutput.Flush();
değişen tek şey datanın yazıldığını garanti ettikten sonra bufferedOutput.Flush(); ‘ı kullanmak. Bir örnek yapmaya gerek yok sanırım.
Text Dosyaları
Eğer okuyacağınız dosyanın text dosyası olduğunu biliyorsanız StreamReader ve StreamWriter sınıflarını kullanabilirsiniz. Bu sınıflar text dosyaların kullanımını dahada kolaylaştırır. Örneğin WriteLine() ve ReadLine() metotları ile tek seferde bir satır yazı üzerinde işlem yapabiliriz.
StreamReader sınıfını örneklemek için ilk önce FileInfo nesnesi oluşturulur ardından OpenText metodu çağrılır.
FileInfo kaynakDosya = new FileInfo(@”c:\deneme\source\text1.txt”);
StreamReader stream=kaynakDosya.OpenText();
Şimdi dosyayı okumaya başlayabiliriz.
Do
{
text=stream.ReadLine();
} while(text!=null)
ReadLine komutu satırın sonuna kadar olan kısmı okur.
StreamWriter writer = new StreamWriter(@”c:\deneme\source\text3.bak”,false);
Şeklinde bir kullanımlada text dosyasını yazmak için kullanabiliriz. Buradaki ikinci argüman false değeri dosyaya ekleme yapılmayacağını ifade ediyor. True olsaydı ekleme amacıyla dosyanın açılacağını belirtirdi. False olması durumunda eğer dosyada önceden bir veri mevcutsa yeni veri eski verilerin üzerine yazılır.
Do
{
text=reader.ReadLine();
writer.WriteLine(text);
Console.writeLine(text);
} while(text!=null)
Yukarıdaki program parçası verinin okunduğu dosyanın sonuna gelinene kadar dosyayı satır satır okuyup bunu önce diğer dosyaya yazar ardından ekrana basar.
Konu karışık olmadığından örnek vermeden yeni başlığa geçmek istiyorum.
Asenkron Dosya İşlemleri
Yukarıda gördüğümüz örneklerin tamamında dosya okuma veya yazma işlemi yapılırken diğer işlemler durdurulur. Dosya ile ilgili iş bitene kadar hiçbir şey yapmamız mümkün değil. Bu türlü senkron okuma yöntemi yerine (ki dosya boyutu çok büyükse uzun müddet beklememiz gerekebilir) dosya ile işlem yapılırken diğer taraftan yeni işlemler yapabilmemiz sağlayan yöntemi, asenkron dosya işlemlerini kullanabiliriz.
Bu yazı dizisinin devamında network üzerinden dosya işlemlerine geçtiğimizde bunun faydasını daha çok göreceğiz.
.NET Framework, Stream sınıfının BeginWrite(), BeginRead() metotlarıyla asenkron I/O işlemlerini yapabilmenizi sağlar.
BeginRead dosyanızı okurken siz diğer yandan işlemlerinizi yapmaya devam edebilirsiniz. Dosya okuma işi bittiğinde haberdar edileceksiniz.
Binary dosyaları okurken kullandığımız 3 parametreye ek olarak burada bir delegate ve birde state nesnesi kullanacağız. Delegate data okunduğunda çağrılacak metottur.
Konuyu açıklamayı bir örnek üzerinde sürdürelim.
Public class AsenkronIO
{
private Stream inputStream;
Private byte[] buffer;
const int BufferSize = 256;
Buraya kadar olan kısım anlaşılır, önceden yaptığımız gibi. Şimdi sınıfın private üyesi olarak delegate’imizi oluşturalım.
private AsyncCallback myCallback;
Bu delegate BeginRead() metodunda kullanılacak olan argümandır. Okuma işlemi bittiğinde çağrılacak metodu belirler. Delegate hakkında daha detaylı bilgiyi www.yazgelistir.com yazı arşivinde bulabilirsiniz.
Şimdi aşağıdaki metodu deklare edip bunu delegateimizle ilişkilendirelim. Bu metot, okuma işi bittiğinde çağrılacak olan metottur.
void OkumaisiBittiginde(IAsyncResult asyncResult);
Sınıfımızın kurucusu içinde;
AsenkronIO()
{
/…
myCallBack = new AsynCallback(this.OkumaisiBittiginde);
}
Şimdi bunun nasıl adım adım çalıştığına bakalım. Main() içinde sınıfın bir örneğini oluşturup çalıştırıyoruz.
public static void Main()
{
AsenkronIO asenkron = new AsenkronIO();
asenkron.Run();
}
asenkron oluşturulurken kullanılan new argümanı sınıfın kurucusunu çağırır. Şimdi kurucu içinde bir dosya açalım ve yukarıdaki Stream nesnesiyle ilişkilendirelim.
AsenkronIO()
{
inputStream=File.OpenRead(@“c:\deneme\source\prince.txt”);
buffer = new byte[bufferSize];
myCallBack = new AsyncCallback(this.OkumaisiBittiginde);
}
Bu örnekte dosya üzerinde işlem yapılırken diğer yandan diğer işlemlerin yapıldığını daha net görebilmeniz için oldukça büyük boyutlu dosyaya ihtiyacımız olacak.
Run metodu içinde asenkron dosya okuma işlemini yapacak olan BeginRead() metodunu çağırıyoruz.
inputStream.BeginRead(
buffer, //okunan kısmın tutulacağı kısım
0, //offset
buffer.Length, //Buffer Boyutu
myCallback, //delegate
null); //state object
Bu örnekte state object için null kullandık. Fakat istediğiniz herhangi bir objeyi kullanmakta serbestsiniz. Bu parametre çağrının yapılacağı durumu belirtmek için kulanılabilir. (durduruldu, çalışıyor vs.)
Dosya işlemi sırasında yapılacak diğer bir işi yazalım. İki işin aynı zamanda yapıldığını daha iyi anlamak için biner biner 500.000e kadar sayan bir döngü kullanalım.
for(long i=0; i<500000;i++)
{
 if(i%1000=0)
 {
  Console.WriteLine(“i:{0}”,i);
 }
}
Okuma işi bittiğinde CLR çağrılacak metodu devreye sokar.
void OkumaisiBittiginde(IAsyncResult asyncResult)
{
ilk önce ne kadar veri okuduğumuzu öğrenmek istersek. EndRead() metodunu kullanacağız.
int okunanbyte=inputStream.EndRead(asyncResult);
EndRead kaç byte okunduğu bilgisini verir. Eğer bu değer sıfırdan büyükse veri okunmuş demektir. Okunan veriyi ekrana yazdırıp tekrar veri okumasını isteyeceğiz.
if(okunanbyte>0)
{
 String s=Encoding.ASCII.GetString(buffer,bytesRead);
 console.WriteLine(s);
 inputStream.BeginRead(buffer,0,buffer.Length,mycallback,null);
}
}
yukarıda açıklamasını yaptığımız kodun tamamını görelim.
using System;
using System.IO;
using System.Threading;
using System.Text;
namespace princeNET
{
 class AsenkronIO
 {
  private Stream inputStream;
  //delegate metodu
  private AsyncCallback myCallback;
  //buffer ve buffer boyutu
  private byte[] buffer;
  const int BufferSize = 256;
  //kurucu metot
  AsenkronIO()
  {
  //inputstreamı açalım
  inputStream=File.OpenRead(@"C:\deneme\source\prince.txt");
  //buffer boyutunu atayalım
  buffer=new byte[BufferSize];
  //callback metonunu ilişkilendirelim
  myCallback=new AsyncCallback(this.okumaisiBittiginde);
  }
  public static void Main(string[] args)
  {
  //sınıfımızın bir örneğini oluşturalım. bu kurucu metodu çağıracak.
  AsenkronIO asenkron = new AsenkronIO();
  //çalıştıralım
  asenkron.Run();
  }
  void Run()
  { //okumaya başlayalım
 
 inputStream.BeginRead(buffer,0,buffer.Length,myCallback,null);
//Yukaridaki metot ilk okuma işi bittiğinde myCallbackin
//ilişkilendirildiği metodu çağırır…
//dosya okunurken bazı işler yapalım
  for(long i=0;i<500000;i++)
  {
  if(i%500==0)
  {
  Console.WriteLine("i:{0}",i);
  }
  }
  }
  //callback metodunu yazalım
  void okumaisiBittiginde(IAsyncResult asyncResult)
  { 
  int okunanbyte=inputStream.EndRead(asyncResult);
  Console.WriteLine(okunanbyte);
  //eğer veri okunmuşsa ekrana basıp tekrar okuyalım
  if(okunanbyte>0)
  {
  String s=Encoding.ASCII.GetString(buffer,0,okunanbyte);
  Console.WriteLine(s);
  //buraya dikkat edin tekrar okuma işi yapıyoruz
  //okuma işi bitince tekrar kendini çağırıyor!!
  inputStream.BeginRead(buffer,0,buffer.Length,myCallback,null);
  }
  }
 }
}
C# ile -yerel- dosya işlemleri konusunu burada bitiriyoruz. Bir sonraki yazı Network I/O. Yeniden buluşmak dileğiyle.