<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>Diyezon &#187; stream</title> <atom:link href="http://www.diyezon.com/tag/stream/feed/" rel="self" type="application/rss+xml" /><link>http://www.diyezon.com</link> <description>programlama sanatı...</description> <lastBuildDate>Tue, 24 Jan 2012 00:13:26 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Stream&#8217;de Uzmanlaşalım&#8230; (Bölüm 2)</title><link>http://www.diyezon.com/streamde-uzmanlasalim-bolum-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=streamde-uzmanlasalim-bolum-2</link> <comments>http://www.diyezon.com/streamde-uzmanlasalim-bolum-2/#comments</comments> <pubDate>Mon, 16 Feb 2009 22:19:27 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[BinaryReader]]></category> <category><![CDATA[BinaryWriter]]></category> <category><![CDATA[BufferedStream]]></category> <category><![CDATA[MemoryStream]]></category> <category><![CDATA[stream]]></category> <category><![CDATA[StreamReader]]></category> <category><![CDATA[StreamWriter]]></category> <category><![CDATA[TBlobStream]]></category> <category><![CDATA[TFileStream]]></category> <category><![CDATA[TMemoryStream]]></category> <category><![CDATA[TResourceStream]]></category> <category><![CDATA[TStream]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=99</guid> <description><![CDATA[Geçen bölümde stream&#8217;e giriş yaptık. Ve değişik veri tipleri ile stream üzerindeki okuma ve yazma işlemlerini gördük. Bu bölümde stream kullanımının sadece dosya okumak-yazmak olmadığını da göreceğiz. Ayrıca .net ve vcl.net ortamında stream kullanımından da bahsetmeye çalışacağız. Hafıza Bölgesinde Stream Kullanımı Takdir edersiniz ki en hızlı stream verisi hafızada bulunan stream verisidir. Yani bir hafıza [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Geçen bölümde stream&#8217;e giriş yaptık. Ve değişik veri tipleri ile stream üzerindeki okuma ve yazma işlemlerini gördük. Bu bölümde stream kullanımının sadece dosya okumak-yazmak olmadığını da göreceğiz. Ayrıca .net ve vcl.net ortamında stream kullanımından da bahsetmeye çalışacağız.</p><p><span
id="more-99"></span></p><h1>Hafıza Bölgesinde Stream Kullanımı</h1><p
align="justify">Takdir edersiniz ki en hızlı stream verisi hafızada bulunan stream verisidir. Yani bir hafıza stream nesnesi her halukarda okuma-yazma işlemleri dosya ve veritabanından hızlı çalışacaktır. Bu durumda eğer bir stream ile çok fazla okuma-yazma işlemleri gerçekleştiriliyorsa, ilk başta bir hafıza streami kullanılmalı ve stream ile işimiz bitip son durumu kaydetmek istediğimizde bu hafıza streaminden başka bir stream nesnesine aktarım yapılması en doğru işlem olacaktır. Aynı şekilde stream verisi üzerinde çok miktarda Seek işlemi yani stream işaretçisinin pozisyonunu değiştirme işlemi gerçekleştiriyorsanız, bu durumda yine stream verisinin tamamını bir hafıza bölgesine aktarmalısınız.</p><p>Aşağıdaki örneğe göz atalım:</p><pre>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  Mem: TMemoryStream;
  writebuffer, readbuffer: AnsiString;
begin
  Mem := TMemoryStream.Create;
  try
    writebuffer := &#039;Diyezon&#039;;
    Mem.WriteBuffer(writebuffer[1], Length(writebuffer));
    Mem.Position := 0;
    SetLength(readbuffer, Length(writebuffer));
    Mem.ReadBuffer(readbuffer[1], Length(readbuffer));
    ShowMessage(readbuffer);
  finally
    Mem.Free;
  end;
end;
</pre></pre><p
align="justify">Gördüğünüz gibi TMemoryStream&#8217;in Create oluşturucusu hiç bir parametre almıyor. Burada ekstradan yazma işleminden sonra Position := 0 ile stream işaretçisini başa getiriyoruz. &Ccedil;ünkü yazma işleminin sonunda işaretçi pozisyonu kayacaktır. Okuma işlemini yapabilmemiz için yine işaretçiyi başa getirip okutuyoruz. Aynı işlemi Seek metodu ile de yapabiliriz. Ek bir not olarak, Seek metodu Position&#8217;dan daha hızlı çalışmaktadır.</p><p
align="justify">Hafıza streami kullanmak, oluşturulması esnasında hiç bir parametre gerektirmediğinden, diğerlerine nazaran daha kolay ve pratiktir. Bu yüzden yapacağımız yüksek yoğunluktaki stream işlemlerini TMemoryStream ile yapmamız bizim yararımıza olacaktır. Özellikle Seek işlemlerinde yani pozisyon değiştirme işlemlerinde hafızadan faydalanmak önemli bir performans artışına sebep olacaktır.</p><h1>Stream&#8217;lerin Aktarımı ve Kopyalanması</h1><p
align="justify">Stream verileri ile okuma-yazma işlemlerinden sonra yapılan en önemli işlem de bir stream&#8217;in başka bir stream verisi üzerine aktarılmasıdır. Mesela bir dosyayı hafızaya almak isteyebilirsiniz. Veya hafızadaki bir veriyi veritabanına aktarmak isteyebilirsiniz. Bu durumda her stream fonksiyonunda bulunan CopyFrom metodu kullanılır.</p><p
align="justify">Aktarım işleminde basit bir metodu kullanmaktan öte, bir noktaya dikkat edilmesi şarttır. CopyFrom metodu ve bunun gibi bir çok metod, stream işaretçisinin bulunduğu pozisyondan itibaren işlemeye başlarlar. Aşağıdaki örneğe bir göz atalım:</p><pre>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  Mem: TMemoryStream;
  F: TFileStream;
  //Mem, F: TStream; şeklinden de yazılabilirdi...
  buffer: AnsiString;
begin
  Mem := TMemoryStream.Create;
  F := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    buffer := &#039;Diyezon&#039;;
    Mem.WriteBuffer(buffer[1], Length(buffer));
    //karakter katarımızı hafızaya aldık.

    Mem.Position := 0; //Önemli!!!!

    F.CopyFrom(Mem, Mem.Size);
  finally
    Mem.Free;
    F.Free;
  end;
end;
</pre></pre><p
align="justify">İlk yaptığımız işlem bir karakter dizisini hafızaya almak oldu. Ardında bu hafıza streamini dosya üzerine kopyaladık. Burada dikkat edilmesi gereken nokta Position := 0 kısmıdır. Hafızaya karakter dizisinin yazılmasından sonra stream işaretçisi sona kayar. Böylece sonrasında gelen CopyFrom gibi metodlar bu pozisyondan itibaren işlem yapmaya başlarlar. İşaretçi sonda olduğu için CopyFrom kopyalayamaz ve bir istisna hatası çıkarır. İşte bunu önlemek için işaretçiyi başa alıyoruz ve hafıza streaminin tamamını dosya üzerine aktarıyoruz.</p><p
align="justify">Kısacası CopyFrom metodu, kopyalanacak olan streamin işaretçi pozisyonundan itibaren, sizin belirlediğiniz uzunluk kadar stream&#8217;den okur ve diğer streame aktarır. Tek dikkat etmemiz gereken nokta kopyalayacağımız kaynak stream&#8217;in mevcut işaretçi poziyonudur. Eğer işaretçi poziyonu sonda ise CopyFrom kaynak stream&#8217;den veri okuyamayacak ve hata meydana gelecektir.</p></p><h1>Nesneler ve Bileşenleri Okuyup Yazmak</h1><p
align="justify">Nesneleri, stream üzerine yazmak denildiğinde ilk akla gelen record verileri gibi okuyup yazmaktır. Halbuki nesneler, her ne kadar benzese de, record verilerinden çok farklıdır ve karışıktır. Bu yüzden direk olarak bir sınıf ya da nesne stream üzerine aktarılamaz. Ancak sizin belirlediğiniz kısımları ayrı ayrı, önceden öğrendiğimiz okuma-yazma işlemleri ile halledebilirsiniz.</p><p
align="justify">Fakat bileşenlerde durum biraz farklıdır. Daha doğrusu TPersistent sınıfından türeyen bütün sınıflarda durum biraz farklıdır. TStream sınıfında, bileşenlerin özelliklerini stream üzerine alabilmek için ReadComponent ve WriteComponent metodları mevcuttur. Bu metodlar sayesinde bileşenler stream verileri üzerine aktarılabilirler. Ki, Delphi IDE&#8217;si bunu bol bol kullanmaktadır. Eğer bileşen yazımı ile ilgili 3 bölümlük makaleyi okumuş iseniz bileşenlerde bir özelliğe kaydedebilme özelliği kazandırmıştık. Yani özelliğin kaydedebilme özelliği yoksa stream üzerine aktarılamazlar. Özelliklerin kaydedilebilme işlemine burada değinmeyeceğiz. Ama isterseniz, bileşen yazımı ile ilgili makaledeki kolleksiyon ve binary özellikler kısmına göz atabilirsiniz.</p><p
align="justify">Bileşenlere bu özelliği kazandıran TPersistent sınıfıdır. TPersistent sınıfı ile ilgili daha fazla bilgi alabilmek için yardım dosyalarına göz atabilirsiniz.</p><p>Yeni bir form açalım ve bir button ekleyelim. Buttonun OnClick olayına aşağıdaki kodları yerleştirelim:</p><pre>
<pre class="brush: delphi">
procedure TForm1.Button1Click(Sender: TObject);
var
  Mem: TMemoryStream;
  F: TFileStream;
begin
  Mem := TMemoryStream.Create;
  F := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Mem.WriteComponent(Button1);

    Mem.Position := 0; //Önemli!!!!

    F.CopyFrom(Mem, Mem.Size);
  finally
    Mem.Free;
    F.Free;
  end;
end;
</pre></pre><p
align="justify">Bu programı çalıştırıp button&#8217;a basarsanız, C: bölümde deneme.txt isimli bir dosya göreceksiniz. Bu dosyayı bir hex editor ile açıp kontrol edebilirsiniz. Ama dosyaya baktığınızda TButton&#8217;a ait tüm özelliklerin olmadığını göreceksiniz. &Ccedil;ünkü burada sadece bileşenin değişmiş olan özellikleri kaydedilir. Her özellik bir default değere sahiptir. Ve bu değer değişmiş ise, WriteComponent metodu bunu stream üzerine yazacaktır.</p><p
align="justify">Şimdi bu dosyadaki Button1 bileşenini okutalım ve form üzerinde oluşturalım. Bunun için yeni bir uygulama açalım ve içinde Button1 isimli bir button olmamasına özen gösterelim. &Ccedil;ünkü birazdan bu bileşeni dosyadan biz oluşturacağız.</p><p
align="justify">Form üzerine bir button ekleyelim ve ismine Button2 diyelim ve Caption olarak &quot;bileşeni dosyadan oku&quot; gibi bir şey yazalım. Ardından buttonun OnClick olayına aşağıdaki kodları yerleştirilem:</p><pre>
<pre class="brush: delphi">
procedure TForm1.Button2Click(Sender: TObject);
var
  F: TFileStream;
  Button1: TButton;
begin
  F := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    Button1 := TButton.Create(Self);
    Button1.Parent := Self;
    F.ReadComponent(Button1);
  finally
    F.Free;
  end;
end;
</pre></pre><p
align="justify">Programı çalıştırıp Button2&#8242;ye tıklarsanız form üzerinde önceden kaydettiğimiz Button1 bileşenini göreceksiniz.</p><p
align="justify">Önceden de dediğimiz gibi TPersistent olmayan nesneleri direk olarak stream olarak yazmak ve okumak mümkün değildir. Fakat kendinizin tanımlayacağı formatta, stream&#8217;den okuma ve stream&#8217;e yazma gibi fonksiyonlar ile nesnelerinize bu özelliği kazandırabilirsiniz. Zaten TPersistent sınıfının da yaptığı işlem budur. TPersistent sınıfına fazla girmek istemiyorum, fakat merakı olanlar TPersistent ile beraber TFiler, TReader gibi sınıflara yardımdan göz atabilirler.</p><p
align="justify">Ek olarak bu konuda söyleyebileceğim bir tecrübem de şudur. İster VCL içinde olsun, ister başka bir yerden bulacağınız bir bileşen veya bir sınıf olsun, eğer stream ile çalışıyorlarsa şu 4 metod bunlarda bulunacaktır. LoadFromStream, LoadFromFile, SaveToStream, SaveToFile.</p><p
align="justify">Buna tecrübe dedim çünkü literatürde bu metodlar aynı isim ve parametrelerle bulunacak diye bir şart yoktur. Fakat VCL&#8217;de bulunan bileşenlerde bolca kullanılan bu metodlar, diğer 3. parti bileşen ve sınıflarda da kolaylık ve alışkanlık olması açısından isimlerini ve yapılarını korumuştur. Eğer bir sınıf stream ile işlem yapıyorsa bilinki bu sınıfta, yukarıda bahsettiğimiz 4 metod bulunacaktır.</p><p
align="justify">Sizler de yazacağınız sınıflarda bu metodları aynı isim ve yapıda kullanırsanız, sınıfınızı ya da bileşeninizi kullanacak olan kişiler zorlanmayacaktır. Bu metodların tanımlamasını VCL&#8217;de herhangi bir sınıf ya da bileşenin tanımlamasından kopyala yapıştır ile alabilirsiniz. Sadece burada değişecek olan kısım LoadFromStream ve SaveToStream metodlarıdır. Bu metodların içeriğini kendinize göre yeniden oluşturmalısınız.</p><pre> </pre></p><h1>Resource Verilerini Okuyup Yazmak</h1><p
align="justify">Resoruce verileri, kısaca açıklamak gerekirse, exe ya da dll dosyanız içine gömülen verilerdir. Bu veriler resim, yazı, icon, form olabileceği gibi herhangibir formatta stream verisi de olabilir.</p><p
align="justify">Resource dosyaları &quot;.RC&quot; uzantılı bir dosyadan herhangi bir resource compiler ile derlenir. Bu derleme sonucunda &quot;.RES&quot; uzantılı bir dosya oluşacaktır. RES dosyalarını istediğiniz bir resource derleyicisi ile oluşturabilirsiniz. &Ccedil;ünkü bu res dosyaları derleyiciden bağımsızdır. Fakat RC dosyasının grameri değişiklik gösterebilmektedir. Biz bu makalede resource verilerini derlemek için delphi ile beraber gelen brcc32 derleyicisini kullanacağız. İsterseniz Visual Studio veya benzeri bir başka resource manager ile RES dosyaları oluşturabilirsiniz. Eğer Delphi 2009&#8242;a sahip iseniz, yeni &quot;Resource Manager&quot; ile daha rahat resource dosyaları oluşturabilirsiniz.</p><p
align="center"><img
width="502" height="331" align="middle" alt="Delphi 2009 - Resource Manager" src="/wp-content/uploads/Image/post99/d2009_resource_manager.jpg" /> <font
size="1"> Delphi 2009&#8242;da menüden Project/Resources&#8230; altından ulaşabilrsiniz.</font></p><p
align="justify">Biz elimizde Delphi&#8217;den başka bir şey olmadığını düşünerek işe koyulalım ve resource dosyamızı oluşturalım.</p><p
align="justify">Notepad ile yeni bir yazı dosyası oluşturalım ve içine bir yerlerden uzun bir paragraf bulup yapıştırılım. Bunu yazi.txt ismi ile kaydelim. Aynı klasöre başka bir yazı dosyası daha oluşturalım ve içine aşağıdakileri yazalım:</p><pre>yazi RCDATA yazi.txt </pre><p
align="justify">Daha sonra bu dosyayı &quot;yazi.rc&quot; ismi ile aynı klasöre kaydedelim.</p><p
align="justify">Burada ilk yazdığımız kelime resource&#8217;a verdiğmiz isimdir. İleride bu isme göre resource verimizi kullanacağız. İkinci kısımda RCDATA ile verimizin tipini belirledik. Burada RCDATA&#8217;nın dışında BITMAP, CURSOR ve ICON gibi ayrı ayrı tipler kullanılabilir. Bunların tam listesi için internette araştırma yapabilirsiniz. (<a
target="_blank" href="http://msdn.microsoft.com/en-us/library/aa381043(VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa381043(VS.85).aspx</a>). En sondaki kısım ise resource olarak kullanacağımız verimizin dosyasını giriyoruz.</p><p
align="justify">Başlat/&Ccedil;alıştır ile cmd yazıp entere basıp komut satırına geçelim. Buradan dosyaları kaydettiğimiz klasöre geçelim(msdos biliyorsunuzdur herhalde <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). Ardından aşağıdaki komutu yazıp entere basalım.</p><pre>brcc32 yazi.rc</pre><p
align="justify">Bir problem oluşmadı ise aynı klasörede yazi.res isimli bir dosya göreceğiz.</p><p
align="justify">Bu işlemlerden sonra Delphi&#8217;ye geçip yeni bir uygulama açalım. Yeni uygulamamızı .res dosyalarını oluşturduğumuz klasöre kaydedelim. Şimdi az önce oluşturduğumuz res dosyasını programımız içine gömmek için gerekli işlemleri yapalım.</p><p
align="justify">Unit1&#8242;in kodlarını açalım ve uygun bir boşluğa aşağıdaki derleyici direktifini girelim:</p><pre>
<pre class="brush: delphi">{$R yazi.res}</pre></pre><p
align="justify">Programızı derlediğimizde bu res dosyasında buluna bütün veriler, resource verisi olarak programımıza gömülecektir. İsterseniz bunu ResHack gibi herhangi bir resource yöneticisi ile kontrol edebilirsiniz.</p><p
align="justify">Şimdi bu gömülmüş olan veriyi okumamız gerekiyor diyelim. Mesela az önce programımız içine gömdüğümüz yazi.txt dosyasını bir memo içine alalım ve gösterelim.</p><p
align="justify">Form üzerine bir adet memo bileşeni ve bir adet button ekleyelim. Button&#8217;unun OnClick olayına şunları yazalım:</p><pre>
<pre class="brush: delphi">var
  Res: TResourceStream;
begin
  Res := TResourceStream.Create(hInstance, &#039;yazi&#039;, RT_RCDATA);
  Memo1.Lines.LoadFromStream(Res);
end;</pre></pre><p
align="justify">Programı çalıştırıp button&#8217;a basalım. Bir sorun çıkmadı ise memo bileşeninde yazi.txt dosyasındaki yazılı olan paragrafı göreceğiz.</p><p
align="justify">Bu yaptığımız örnek ile görüyoruz ki; bir TResourceStream nesnesi, herhangi bir exe veya dll içindeki resource verilerine erişebiliyor. Create oluşturucusunda gördüğümüz ilk parametre resource verisini nereden çekeceğimizi belirliyor. Buna daha sonra değineceğiz. İkinci parametre olarak erişeceğimiz resource verisinin ismidir. Bunu RC dosyasını hazırlarken görmüştük. Aynı şekilde son parametre de RC dosyasında belirlenmişti. Bu son parametre ile resource verisinin tipini belirliyoruz. Tek fark olarak baş tarafına RT_ ön ekini getiriyoruz.</p><p
align="justify">Sonraki işlem ise memo bileşenin Lines özelliğine streamdeki veriyi aktarıyoruz.</p><p
align="justify">Bu mantığı kullanarak istediğiniz türde veriyi resource olarak gömebilir ve program çalışması anında kullanabilirsiniz. Bu yaptığımız örnek sadece mevcut programdan verileri çekebilir. Fakat başka bir dll ya da exe&#8217;den veri çekmemiz gerektiğinde ne yapacağız?</p><p
align="justify">İşte bu durumda hInstance olarak girdiğimiz parametre değişecektir. &Ccedil;ünkü burada girdiğimiz parametre veriyi nereden çekeceğimizi daha doğrusu resource verisinini nerede olduğunu belirler.</p><p
align="justify">Her program(exe, dll, ocx, ..) çalıştırıldığında windows tarafından bir handle numarası ile ilişkilendirilir. Delphi&#8217;de bu handle numarasına erişebilmek için hInstance isimli global bir değişkeni kullanıyoruz. Bu yüzden Create oluşturucusunda mevcut çalışan programımızın handle numarasını buraya birinci parametre olarak girdik.</p><p
align="justify">Peki başka bir exe veya bir dll&#8217;in handle numarasını nasıl alırız?</p><p
align="justify">Bunun için cevabımız LoadLibrary fonksiyonudur. Bu fonksiyon ile çağırdığımız herhangi bir program geçici olarak hafızaya yüklenir ve windows tarafından bir handle atanır. Bu handle numarası ise çıkış olarak döner. Eğer yükleme işlemi başarısız ise handle numarası 0 olarak dönecektir. Mesela yukarıda yaptığımız işlemleri bir dll içinden almaya kalkarsak nasıl yapacaktık?</p><pre>
<pre class="brush: delphi">var
  Res: TResourceStream;
  DLLHandle: THandle;
begin
  DLLHandle := LoadLibrary(&#039;resource.dll&#039;);
  if DLLHandle &lt;&gt; 0 then
  begin
    Res := TResourceStream.Create(DLLHandle, &#039;yazi&#039;, RT_RCDATA);
    Memo1.Lines.LoadFromStream(Res);
  end;
end;</pre></pre><p
align="justify">Aynı işlemi exe, ocx gibi diğer çalıştırılabilir formatlar içinde yapabilirsiniz.</p><p
align="justify">Artık herhangi bir veriyi exe içine gömüp okumayı öğrendik. Şimdi de bu işlemin bir benzerini veritabanlarında yapalım.</p></p><h1>Veritabanında Stream Kullanımı</h1><p
align="justify">Artık buraya kadar öğrendiklerimizle herhangi bir stream verisini okuyup yazabiliriz. Burada yapacağımız işlem de pek farklı olmayacak. Bu yüzden hemen konuya girelim ve kısaca veritabanlarında nasıl stream kullanıldığını görelim.</p><p
align="justify">Veritabanlarında bulunan tablolarda stream verilerini saklamak için alanımız BLOB tipinde olmalıdır. BLOB tipindeki alanlar binary veriler tutabilir. Fakat indeksli ve üzerinde çok arama tarama işlemleri yapılan bir tablomuz var ise bu BLOB alanlar veritabanı işlemlerini yavaşlatacaktır. Bu yüzden bu BLOB alanları başka bir tablo üzerine taşımak performansı artıracaktır.</p><p
align="justify">Tabloda bulunan bir BLOB alana veri yazmak ve bu alandan veri okumak için TBlobStream nesneleri kullanılır. Kullanımı aşağıdaki gibidir:</p><pre>
<pre class="brush: delphi">
var
  blob: TBlobStream;
begin
  blob := tblBirTablo.CreateBlobStream(tblBirTablo.FieldByName(&#039;BlobAlan&#039;), bmReadWrite);
  try
    blob.Position := 0;
    blob.ReadBuffer(...);
    blob.WriteBuffer(...);
    ......
  finally
    blob.Free;
  end;
end;
</pre></pre><p
align="justify">Genel manada kullanım bu şekildedir fakat daha başka yöntemler ile de blob stream oluşturulabilir. Bu örnekte BlobAlan isimli bir alan hem okuma hem de yazma işlemleri için stream olarak bizim blob stream nesnemize atanmıştır. Burada sadece okuma işlemleri yapacaksak bmRead, sadece yazma işlemi yapacaksak bmWrite parametrelerini kullanmalıyız.</p><h1>TStream&#8217;i .NET ve Delphi 2009&#8242;da Kullanmak</h1><p
align="justify">TStream sınıfı Delphi&#8217;nin bileşen mantığının temelini oluşturmaktadır. Delphi, .NET ortamına taşınırıken haliyle TStream sınıfı da ufak farklılıklar ile .NET ortamına taşınmıştır. Benim burada bahsedeceğim konu sadece bu küçük farklılıklar olacaktır. Geri kalan işlemlerde buraya kadar öğrendiğiniz bilgiler fazlasıyla yetecektir.</p><p
align="justify">.NET ortamında Pointer işlemleri kullanılmadığından TStream sınıfı ve alt sınıfları için bir değişikliğe gidilmesi şarttı. Bu yüzden, TStream sınıfı .NET ortamına taşındığında Pointer işlemleri yerine, bu işlemlerin yerini tutan belirgin ve basit tipler için ayrı ayrı metodlar hazırlandı.</p><p
align="justify">Mesela hatırlarsanız normalde TStream sınıfında bulunan ReadBuffer ile bir srting veri yazdığımızda string&#8217;i pointer olarak type cast yapıp parametre olarak giriyorduk. .NET&#8217;de pointer kullanmadığımız için ReadBuffer yerine artık aynı isimli ve sadece özel tipler için özdeşleştirilmiş bir metodumuz var. Bu metodu kullanarak rahat bir şekilde pointer kullanmadan işlemlerimizi aynı şekilde yapabiliriz. Tabi bu daha önce yaptığımız Win32 TStream sınıfına göre çok rahatlık getirmektedir. Bu da hiç şüphesiz .Net&#8217;in getiridiği rahatlıklardan birisi.</p><p
align="justify">ReadBuffer ver WriteBuffer metodlarından bir kaçındaki değişlikliklere göz atalım:</p><pre>
<pre class="brush: delphi">
    //TBytes
    procedure ReadBuffer(Buffer: TBytes; Count: Longint); overload;
    procedure WriteBuffer(const Buffer: TBytes; Count: Longint); overload;

    //Boolean
    procedure ReadBuffer(var Buffer: Boolean); overload;
    procedure WriteBuffer(const Buffer: Boolean); overload;

    //Integer
    procedure ReadBuffer(var Buffer: Integer); overload;
    procedure WriteBuffer(const Buffer: Integer); overload;
    procedure WriteBuffer(const Buffer: Integer; Count: Longint); overload; platform;

    //Char
    procedure ReadBuffer(var Buffer: Char); overload;
    procedure ReadBuffer(var Buffer: Char; Count: Longint); overload; platform;
    procedure WriteBuffer(const Buffer: Char); overload;
    procedure WriteBuffer(const Buffer: Char; Count: Longint); overload; platform;

    // AnsiChar
    procedure ReadBuffer(var Buffer: AnsiChar); overload;
    procedure ReadBuffer(var Buffer: AnsiChar; Count: Longint); overload; platform;
    procedure WriteBuffer(const Buffer: AnsiChar); overload;
    procedure WriteBuffer(const Buffer: AnsiChar; Count: Longint); overload; platform;
</pre></pre><p
align="justify">Tam liste için lütfen Borland.Vcl.Classes unitinde bulunan TStream sınıfına ya da yardıma göz atınız.</p><p
align="justify">Gördüğünüz gibi aynı ReadBuffer metodu, overload yöntemi ile bir çok kez tanımlanmış. Biz bunlardan ihtiyacımız olanı seçip kullanacağız. Boolean ve Integer yazımı için tek bir adet parametre gerektirdiğine dikkat ediniz.</p><p
align="justify">Diyelimki stream üzerine yazdıracağımız veya okuyacağımız veri çeşidine uygun bir metod bulunmamakta. Bu durumda verimizi bir byte dizisi gibi düşünüp ona göre davranacağız. Bu durumda verimizi TBytes&#8217;a yani &quot;array of Byte&quot; tipinieçevirmemiz gerekmektedir. Bunun için herhangi bir for döngüsü tanımlayarak bu işlememizi yapabiliriz.</p><p
align="justify">Integter ile ilgili metodlara bakarsanız, en sonda fazladan Count parametresi alan bir metod yerleştirdim. Bütün metod çeşitlerinde alternatif olarak bu ikininci parametreye sahip olan metodlar mevcuttur. Bu parametreyi mesela Integer için normalde 4 byte ayrılmasına rağmen siz 10 byte yer ayırmak istediğinizde kullanablirsiniz.</p><p
align="justify">Bu metodlara baktığınızda string ile ilgili bir özel metodun olmadığını farkedeceksiniz. Peki string veriyi nasıl yazacağız?</p><p
align="justify">Bu durumda veriyi hangi encode&#8217;da yazacağımız veya okuyacağımız önemli. Veriyi istersek UTF8 formatında istersek UTF16 veya ANSI olarak okuyabilir ve yazabiliriz. Bu durumda string verimizi byte dizisine çeviren veya byte dizisini string verisine çeviren bir yönteme ihtiyacımız var. Aşağıdaki örneğe göz atalım:</p><pre>
<pre class="brush: delphi">var
  Mem: TMemoryStream;
  s: string;
  bytes: TBytes;
begin
  Mem := TMemoryStream.Create;
  try
    s := &#039;diyezon&#039;;
    bytes := Encoding.UTF8.GetBytes(s); //uses&#039;a System.Text eklenmeli
    Mem.WriteBuffer(bytes, Length(bytes));
    Mem.Position := 0;
    Memo1.Lines.LoadFromStream(Mem);
  finally
    Mem.Free; //Yapmanıza gerek yok.
  end;
end;</pre></pre><p
align="justify">Gördüğünüz gibi string verimizi utf-8 olarak stream üzerine attık ardından memo1 ile ekranda gösterdik. Bu işlemi yapabilmek için string verisini uygun bir byte dizisine çevirmemiz gerkiyordu. Bunun için System.Text alanında bulunan Encoding sınıfını kullanıyoruz. Bu sınıfı kullanabilmek için uses&#8217;a System.Text&#8217;i eklemelisiniz.</p><p
align="justify">İsterseniz bu yazma işlemini Encoding.Unicode ile utf-16 ya da diğer adıyla unicode olarak, isterseniz Encoding.Default ile işletim sisteminin varsayılan ansi karakterleri olarak yazıp okuyabiliriz. Önemli olan string bir şekilde byte dizisine çevrilmiş olması gerekmektedir. Ayrıca yazma ve okuma işlemlerinde aynı tip encode formatını kullandığınızdan emin olun. Yani ya her ikisinde de Unicode ya da her ikisinde de UTF8 kullanın.</p><p
align="justify">Eğer TBytes&#8217;dan stringe çevirim yapmamız gerekirse bu durumda aşağıdaki metodu kullanıyoruz:</p><pre>
<pre class="brush: delphi">s := Encoding.UTF8.GetString(bytes)</pre></pre><p
align="justify">Gördüğünüz gibi .net ile bu işlemleri yapmak çok basit. Ayrıca en sonda stream&#8217;i free yapmasanız da Garbage Collector sizin için bunu yapacaktır. Fakat TFileStream kullanıyor ve dosyaya fmShareExclusive gibi kilitleyici bir parametre ile ulaşıyorsanız, stream nesnesini öldürmeden veya program kapanmadan, başka bir program dosyaya ulaşamaz, dosya kilitli kalır.</p><p
align="justify">Bu bölümde bahsetmek istediğim başka bir konu da bu işlemleri Delphi 2009 ve üstünde yapmak. Burada bahsediyorum, çünkü bahsedeceğimiz bu konu .net ile de ilgili bir konu.</p><p
align="justify">Aslında daha önceden gördük ki; bir stringi stream&#8217;e yazmak için stream&#8217;in kaç karakter olduğundan çok ,boyutunu bilmemiz gerekiyor. Bu yüzden ansi ya da ascii stringlerde boyut olarak her zaman Length(string) şeklinde aldık. &Ccedil;ünkü ansi stringlerde boyut = karakter sayısıdır. Fakat unicode olan stringlerde bu durum biraz farklıdır. Unicode olan stringler 1 byte değil, 2 byte yer kaplarlar. Bu yüzden WideString gibi unicode stringler ile uğraştığımızda artık boyutumuz Length*2 olmaktadır. Bildiğiniz gibi Delphi.Net ve Delphi 2009 ve yukarısı derleyicilerde &quot;string&quot; ve &quot;Char&quot; tipi unicode formatındadır. Bu derleyicilerde ansi olarak veri tanımlamamız gerektiğinde AnsiString, AnsiChar, PAnsiChar gibi tipleri kullanmalıyız. Aksi durumda bu derleyicilerde bir Char tipi 2 byte yer kaplar. Bu konu ile ilgili daha önceden örnek verdiğimizden bu konuyu burada bitiriyorum.</p></p><h1>.NET Stream Nesnesinin Kullanımı</h1><p
align="justify">TStream&#8217;e ek olarak, .net&#8217;de aynı mekanizma bulunmaktadır. Neden bilmiyorum ama .net&#8217;de bulununa bir çok teknoloji Delphi ile benzerlik göstermektedir. Belki de c# ve .net&#8217;in fikir babası olan Anders Hejlsberg&#8217;in delphi&#8217;nin ilk temellerini atmasından kaynaklanıyor olabilir. Her neyse biz konumuza dönelim.</p><p
align="justify">Aslında bu konu çok uzun anlatılabilecek bir konu ve burada bahsetmemiz makaleyi bir üçüncü bölüme bile taşıyabilir. Ki bence buna gerek yok. Ben sadece TStream ile arasındaki benzerlikleri gösterip, nasıl kullanıldığına dair bir örnek vermek istiyorum.</p><p
align="justify">Bu bölümü VCL.Net kullanmak istemeyip saf .net kodu oluşturmak isteyenler veya Delphi Prism kullananlar için yazmak istedim. Aksi takdirde TStream sınıfı çoğu kez işimizi rahat bir şekilde görmektedir. Fakat bazen kullandığımız .net sınıfları .net&#8217;in stream sınıflarına ihtiyaç duyabilmektedir.</p><p
align="justify">VCL&#8217;de bulunan bazı stream sınıfları yerine .net&#8217;de bulunan karşılıklarını şöyle sıralayabilriiz:</p><pre>TFileStream =&gt; FileStream
TMemoryStream =&gt; MemoryStream
TIdTCPClient =&gt; NetwrokStream</pre><p>vs..</p><p
align="justify">Tabi bu stream sınıflarının metodları benzerlik gösterse de kullanımları farklıdır. Bu stream sınıflarını kullanabilemek için uses&#8217;a System.IO alanını eklemelisiniz. Biz buradan MemoryStream ile ilgili bir örnek verelim:</p><pre>
<pre class="brush: delphi">var
  Mem: MemoryStream;
  s: string;
  bytes: array of Byte;
begin
  Mem := MemoryStream.Create;
  s := &#039;diyezon&#039;;
  bytes := Encoding.UTF8.GetBytes(s);
  Mem.Write(bytes, 0, Length(bytes));</pre></pre><p
align="justify">Gördüğünüz gibi kullanım olarak TStream&#8217;den farklı değil. Fakat Write metodunu TStream&#8217;de olduğu gibi bir çok çeşidi olduğunu zannetmeyin sakın. &Ccedil;ünkü MemoryStream sınıfında Write metodunun sadece iki adet varyasyonu mevcut. Bu metodda ilk parametre byte dizisi, ikinci parametre nereden itibaren yazılacağı yani ofset ve üçüncü parametre olarak yazdırılacak verinin uzunluğu giriliyor.</p><p
align="justify">Bunun dışında bu string ve binary değerleri rahat bir şekilde okuyup yazabilmek için çeşitli ek sınıflar kullanılmaktadır. Mesela BinaryWriter ile binary değerleri stream üzerine yazabiliriz veya StreamReader ile streamden karakter verilerini okuyabiliriz. Aşağıdaki örneğe göz atalım:</p><pre>
<pre class="brush: delphi">var
  Mem: MemoryStream;
  textW: StreamWriter;
  s: string;
  bytes: array of Byte;
begin
  Mem := MemoryStream.Create;
  textW := StreamWriter.Create(Mem);
  s := &#039;diyezon&#039;;
  textW.Write(s);</pre></pre><p
align="justify">Yukarıda yaptığımız işlemin aynısını burada StreamWriter sınıfı ile gerçekleştirdik. İşte burada StreamWriter sınıfının Write metodunda bir çok varyasyon mevcuttur. Bu varyasyonlara yardım dosyalarından ulaşabilirsiniz. Mesela bir Integer değer yazmak için yine StreamWriter&#8217;a ait Write metodunu kullanmamız kafidir.</p><p
align="justify">Bunun dışında bir de kullanabileceğimiz BinaryWriter ve BinaryRead sınıfları mevcuttur. Bu sınıflar ise binary verileri okuyup yazmak için kullanılırlar. Ama kullanım aynı olduğu için bu konuya da girmeyeceğim.</p><p
align="justify">Burada en son olarak BufferedStream sınıfından bahsetmek istiyorum. Bu stream ile okuma ve yazma işlemlerini başka bir stream üzerinde buffer&#8217;layarak daha hızlı işlemler yaptırabilirsiniz. Hatırlarsanız TMemoryStream sınıfından bahsederken, bu sınıfı diğer stream sınıflardaki okuma yazma işlemleri yerine kullanılabileceğinden ve bunun performansı artırdığından bahsetmiştik. İşte burada TMemoryStream bir buffer vazifesi görmektedir.</p><p
align="justify">Mesela bir dosyaya bir çok veri yazılacak, ve bu veriler parça parça yazılacak. Ayrıca bu dosya stream&#8217;i üzerinde bir çok seek işlemi yani ileri geri alma işlemleri yapılacak. İşte bu durumda direk olarak TFileStream gibi bir dosya stream nesnesi ile uğraşmak yerine hafıza stream&#8217;leri ile uğraşmamız işlemlerimizin performansını önemli derecede artıracaktır. Yapacağımız stream işlemlerini bir hafıza stream&#8217;i üzerinde gerçekleştirip, en son işlemimiz bittiğinde hafıza stream&#8217;ini ilgili stream nesnesine aktarmak en akıllıca yöntemdir. İşte bu yönteme buffering ya da tamponlama tekniği denir.</p><p
align="justify">İşte bu anlattığımız tekniği BufferedStream nesnesi gerçekleştirmektedir. Aynı işlemi ekstra bir memory stream tanımlayarak siz de oluşturabilirisiniz, hatta bu makalede buna benzer örnekleri çok gördük.</p><p
align="justify">BufferedStream sınıfını kullanmak isteyenler, yardım dosyalarında ve msdn&#8217;de tanımlamasına bakabilirler. Artık stream mantığını kafanızda yerleştirdiğinize göre burada kullanımını vermek artık sıkacaktır.</p><h1>Sonuç</h1><p
align="justify">Bir makalenin daha sonuna geldik. Bu makale serisinde VCL ve .net&#8217;de kullanılan stream sınıflarına değinmeye çalıştık ve bu sınıfların kullanımına dair çeşitli örnekler vermeye çalıştık.</p><p
align="justify">Tabi ki burada verdiklerimiz stream için en temel konulardır. Bu makaleyi okuduktan sonra artık kafanızda stream&#8217;e ait somut düşüncelerin oluştuğunu zannediyorum. Ve bundan sonra önünüze gelecek olan bir çok stream işlemini rahat bir şekilde halledebileceğinizi umut ediyorum.</p><p
align="justify">Fikir, eleştiri ve yorumlarınızı bekliyorum. Ayrıca makaledeki hataları bildirirseniz sevinirim.</p><p>Hayırlı çalışmalar.</p><p
align="center"><strong>Fatih Tolga ATA, Şubat 2009</strong></p></p></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/streamde-uzmanlasalim-bolum-2/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Stream&#8217;de Uzmanlaşalım&#8230; (Bölüm 1)</title><link>http://www.diyezon.com/streamde-uzmanlasalim-bolum-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=streamde-uzmanlasalim-bolum-1</link> <comments>http://www.diyezon.com/streamde-uzmanlasalim-bolum-1/#comments</comments> <pubDate>Sat, 13 Sep 2008 07:15:49 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[stream]]></category> <category><![CDATA[TBlobStream]]></category> <category><![CDATA[TFileStream]]></category> <category><![CDATA[TMemoryStream]]></category> <category><![CDATA[TResourceStream]]></category> <category><![CDATA[TStream]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=62</guid> <description><![CDATA[Diyezon&#8217;a yazmayalı baya bir süre olmuştu. Neredeyse diyezon, kendi işlerim yüzünden hayalet site olma yolunda ilerliyordu. Bundan önceki son yazımı yaklaşık bir sene önce yayınlamıştım. Her neyse&#8230; Stream&#8217;ler hakkında bir makale yazmak, yapacaklarım arasındaydı. En son gelen bir istek üzerine stream konusuna el atalım istedim. Bu makale serimizde hem VCL&#8217;de bulunan TStream, hem de .NET&#8217;de [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Diyezon&#8217;a yazmayalı baya bir süre olmuştu. Neredeyse diyezon, kendi işlerim yüzünden hayalet site olma yolunda ilerliyordu. Bundan önceki son yazımı yaklaşık bir sene önce yayınlamıştım. Her neyse&#8230;</p><p
align="justify">Stream&#8217;ler hakkında bir makale yazmak, yapacaklarım arasındaydı. En son gelen bir istek üzerine stream konusuna el atalım istedim. Bu makale serimizde hem VCL&#8217;de bulunan TStream, hem de .NET&#8217;de bulunan Stream sınıfları ve kullanımlarından bahsetmeye çalışacağım.</p><p
align="justify">Özellikle TStream üzerinde stringlerle uğraşmak gibi bir çok konu programcıların kafasını karıştırabilmektedir. Bu ve bunun gibi sorunların çözümlerine de çareler bulmaya çalışacağız.</p><p><span
id="more-62"></span></p><h1>Stream Nedir?</h1><p
align="justify">Bilgisayar ortamında yazma-okuma işlemlerinden bahsettiğimizde aklımıza bir çok şey gelir. Mesela disk üzerinde bir dosyaya erişip üzerinde yazma ve okuma işlemleri yapabiliriz. Veya bir hafıza bölgesinde yazma-okuma işlemleri yapabileceğimiz gibi bir veritabanındaki bir tablo alanında veya daha başka çeşit çeşit ortamlar üzerinde yazma-okuma işlemleri yapabiliriz. Bu bütün yazma-okuma işlemleri aslında her zaman yöntemi, işleyişi aynı olan bir fonksiyonlar dizisidir. Sadece alt planda yazma-okuma işleminin yapılacağı yüzeyde(disk, hafıza&#8230;) farklılıklar oluşmaktadır.</p><p
align="justify">İşte bu işlemleri kolay bir şekilde, ortak bir sınıf üzerinden yapmak istediğimiz zaman ortaya Stream gibi bir yapı çıkıyor. Stream nesnelerini kullanarak, bir dosyayı yazıp okuyabilir, aynı fonksiyonları kullanarak bir hafıza bölgesine ulaşılabilir veya yine aynı fonksiyonlar ile bir veritabanı tablosunun alanına ulaşılabilir.</p><p
align="justify">Stream, burada anlatılan basit özelliklerin yanında, stream verileri üzerinde bir çok işlemi yapabilmemize imkan sağlamaktadır. Bu makalemizde bunları ayrıntısı ile görmeye çalışacağız.</p><h2>Stream Verileri</h2><p
align="justify">Stream nesneleri, stream verilerini kullanmamızı sağlamaktadır. Stream verileri, üzerinde çalışılan yere göre çeşitli veriler içermektedir. Bu bir resim dosyası olabileceği gibi kendimize özel çok farklı bir format da olabilir.</p><p
align="justify">Buradan da anlaşılacağı gibi, stream verileri bir nevi bir byte dizisidir. Yani aynı veriyi &quot;<font
face="Courier New"><strong>array of</strong> byte</font>&quot; şeklinde kullanarak da oluşturabilir ve üzerinde işlem yapabiliriz. Fakat bu, bize ek masraftan başka bir şeye yaramayacaktır. &Uuml;stelik yazacağımız fonksiyon ya da sınıf stream nesneleri gib esnek ve hızlı olmayacaktır.</p><h2>Stream Ne Zaman Kullanılmalı?</h2><p
align="justify">Yapacağımız işlemerde stream mi, array mi kullanılmalı noktasında karar vermek istediğimizde bir kaç hususu göz önünde bulundurmamız gerekecektir.</p><p
align="justify">İlk olarak bilmemiz gereken stream verileri array&#8217;de olduğu gibi indeksli <strong>değildir</strong>. Eğer kullanacağımız veriler, sabit veri tipi ise ve bir for döngüsüne girmek üzere oluşturulacak ise kullanacağımız nesne array olacaktır. Stream&#8217;de de istediğiniz veriye array&#8217;de olduğu gibi ulaşabilirsiniz. Fakat bu ileride de göreceğimiz gibi array indekslerinden farklı olarak çalışmaktadır.</p><p
align="justify">Eğer bu veri dizimiz bir disk veya bir hafıza bölgesi üzerine yazılacaksa ve burada işlem yapılacaksa, bu durumda array yerine stream kullanılması bize bir çok kolaylık sunacaktır.</p><p
align="justify">Aynı şekilde veri dizimiz sabit veri tipleri içermiyorsa yani sadece <strong><font
face="Courier New">array of string</font></strong> veya sadece <font
face="Courier New"><strong>array of</strong> Integer</font> değilse bu durumda yine stream kullanmalıyız. Mesela veri dizimizin birinci elemanı 4 karakterlik bir <strong><font
face="Courier New">string</font></strong>, ikinci elmanı bir <font
face="Courier New">Integer</font>, üçüncü elemanı bir <font
face="Courier New">Boolean</font>, vs&#8230; gibi. Bu şekilde kendi dosya formatımızı oluşturabileceğimiz gibi, doc, exe, zip gibi bir çok binary dosyasının yapısını bildiğimizde bu dosyaların verilerine de ulaşabiliriz.</p><h1>VCL&#8217; de Stream Kullanımı</h1><p
align="justify">İlk başta VCL ile stream kullanımına değinelim. Ardında .Net üzerinde stream kullanımını göreceğiz.</p><p
align="justify">VCL ile stream işlemleri yapabilmek için <font
face="Courier New">TStream </font>sınıfından türeyen bir çok sınıf mevcuttur. <font
face="Courier New">TStream </font>sınıfı <strong>abstract </strong>olarak tanımlanmış bir sınıftır ve direk olarak kullanılamaz. Abstract olarak oluşturulmuş olan bir sınıftan <strong>nesne oluşturulamaz.</strong> Ancak bu abstract sınıftan türemiş olan <strong>abstract olmayan sınıflardan</strong> nesne oluşturulabilir. Bu şekilde bir yapı olması, tüm stream sınıflarının aynı olan bir çok metod ve özelliğe sahip olmasını sağlar. Abstract sınıfların avantajları burada anlatılacak kadar kısa değil. Bu yüzden şimdilik bu kadar bilmemiz yeterli.</p><p
align="justify">Help dosyasına göz attığımızda <font
face="Courier New">TStream </font>sınıfından türemiş bir çok stream sınıfını görüyoruz.</p><p
align="justify"><font
face="Courier New">TFileStream</font>: Disk yani <strong>dosya sistemi</strong> üzerinde bulunan dosyalarla işlem yapmamızı sağlar.</p><p
align="justify"><font
face="Courier New">TMemoryStream</font>: Dosyalar üzerinde yapılan aynı işlemleri bir <strong>hafıza bölgesi</strong> üzerinde yapmamızı sağlar.</p><p
align="justify"><font
face="Courier New">TBlobStream</font>: Veritabanında bulunan bir tablodaki <font
face="Courier New">BLOB</font> yani binary veri olarak tanımlanmış alanlar üzerinde okuma-yazma işlemleri yapmamızı sağlar.</p><p
align="justify"><font
face="Courier New">TResourceStream</font>: Resource dediğimiz, exe, dll gibi çalıştırılabilir dosyalar içine gömülü olan resim, yazı, dialog kutuları, versiyon bilgisi gibi çok çeşitli verileri okuyup yazmamıza imkan sağlamaktadır.</p><p
align="justify"><font
face="Courier New">TStringStream</font>: Aynı işlemleri bir string verisi üzerinde işlem yapabilmemizi sağlar. Böylece, sınırsız uzunlukta bir string üzerinde stream ile yapılabilecek her türlü işlemler yapılabilir.</p><p
align="justify">Bunlar, temelde kullanılan stream çeşitleridir. Bunun gibi özel olarak tasarlanmış bir çok stream sınıfı da olabilir. Fakat hepsi TStream sınıfından türediği için, kullanımları da aynı olacaktır.</p><h1>Dosyalar &Uuml;zerinde Stream İşlemleri</h1><p
align="justify">Dosyalar ile uğraşırken <font
face="Courier New">TFileStream </font>kullanıldığını söylemiştik. <font
face="Courier New">TStream </font>sınıflarının nasıl kullanıldığını, sonucu kolay görme açısından ilk başta dosyalar üzerinde görelim. İleride diğer stream çeşitlerine de değineceğiz.</p><pre>
<pre class="brush: delphi">
var
  AFile: TFileStream;
  Buffer: String;
begin
  AFile := TFileStream.Create(&#039;C:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Buffer := &#039;Deneme&#039;;
    AFile.WriteBuffer(Pointer(Buffer)^, Length(Buffer));
  finally
    AFile.Free;
  end;
</pre></pre><p
align="justify">Bu kodları bir button&#8217;nun <font
face="Courier New">OnClick </font>olayına yerleştirip deneyelim. Kodlar çalıştırıldığında, <font
face="Courier New">C: </font>üzerinde eğer bir erişim ihlali yoksa <em>deneme.txt</em> isminde bir dosya göreceğiz.</p><p
align="justify">İlk bakışta bu kodlar ürkütücü gelebilir. Fakat konular ilerledikçe stream kullanmanın ne kadar rahat olduğunu göreceğiz.</p><p
align="justify"><font
face="Courier New">AFile</font>&#8216;ın oluşturulduğu satıra bakalım. Bir <font
face="Courier New">TFileStream </font>nesnesi oluşturabilmeniz için iki adet parametreye ihtiyacınız var. Birincisi <strong>dosyanın yolu</strong>, ki bizim örneğimizde &quot;<em>deneme.txt</em>&quot; olarak verilmiş. İkinci parametremiz ise bu dosya üzerinde yapacağımız <strong>işlem kipini ve paylaşım kipini</strong> belirler. İşlem ve paylaşım kipleri &quot;<font
face="Courier New"><strong>or</strong></font>&quot; kelimesi ile birleştirilerek kullanılır. Şimdi bu kiplerin ne olduğuna bakalım:</p><p><strong>İşlem Kipleri: </strong></p><p
align="justify"><font
face="Courier New">fmCreate</font>: Verilen dosya yolunda eğer dosya yoksa oluşturur. Eğer dosya var ise üzerine yazar.</p><p
align="justify"><font
face="Courier New">fmOpenRead</font>: Dosyayı sadece okuma amaçlı açar.</p><p
align="justify"><font
face="Courier New">fmOpenWrite</font>: Dosyayı sadece yazma amaçlı açar.</p><p
align="justify"><font
face="Courier New">fmOpenReadWrite</font>: Dosyayı hem okuma hem de yazma için açar.</p><p><strong>Paylaşım Kipleri:</strong></p><p
align="justify"><font
face="Courier New">fmShareExclusive</font>: Diğer uygulamaların, bizim üzerinde çalıştığımız dosya üzerinde hem <strong>okuma </strong>hem de <strong>yazma</strong> işlemlerini <strong>yasaklar</strong>.</p><p
align="justify"><font
face="Courier New">fmShareDenyWrite</font>: Diğer uygulamalar dosyayı okuyabilir fakat üzerinde <strong>yazma </strong>işlemi <strong>yapamazlar</strong>.</p><p
align="justify"><font
face="Courier New">fmShareDenyRead</font>: Diğer uygulamaların, dosya üzerinde <strong>okuma </strong>işlemi yapmasını <strong>yasaklar</strong>.</p><p
align="justify"><font
face="Courier New">fmShareDenyNone</font>: Ne okuma ne de yazma yasağı koyar. Yani diğer uygulamalar, dosya üzerinde yazma ve okuma yapabilirler.</p><p
align="justify"><font
face="Courier New">fmShareCompat</font>: Eskiden dos zamanlarında kullanılan <em>FCB(File Control Block)</em> ile uyumluluğu sağlamak için kullanılır. <em>Windows </em>üzerinde <font
face="Courier New">fmShareExclusive</font>, <em>Linux </em>üzerinde ise <font
face="Courier New">fmShareDenyNone </font>ile aynıdır. Bu yüzden bunu es geçebilirsiniz.</p><p
align="justify">Yukarıda verdiğimiz örnekte olduğu gibi işlem kipleri ve paylaşım kipleri &quot;<font
face="Courier New"><strong>or</strong></font>&quot; kelimesi ile birleştirilerek kullanılır. Örneğimizde dosya <font
face="Courier New">fmCreate </font>ile eğer yoksa oluşturulacak ve eğer varsa üzerine yazılacaktır. Ve dosya <font
face="Courier New">fmShareDenyNone</font> kipinde olduğu için üzerinde herhangi bir okuma yazma yasağı bulumamaktadır.</p><p
align="justify">Ardından <strong><font
face="Courier New">try..finally</font></strong> buloğunda, stream üzerine yazma işlemleri yapılmaktadır. Şimdilik bu kısmı anlatmayacağız. &Ccedil;ünkü okuma ve yazma işlemleri için ayrı bir konu başlığımız bulunmaktadır.</p><p
align="justify"><font
face="Courier New"><strong>finally</strong></font> bloğunda gördüğümüz <font
face="Courier New">Free </font>metodu dosyalar üzerinde fazladan bir önem arz etmektedir. &Ccedil;ünkü bir kez <font
face="Courier New">TFileStream </font>ile bir dosyaya erişildiğinde, bu stream nesnesi <font
face="Courier New">Free </font>oluncaya yani yok edilinceye kadar, dosyanın handle&#8217;ı işletim sistemi tarafından tutulur. Dosya handle&#8217;ı aktif olduğu sürece dosyanın silinmesi taşınması işletim sistemi tarafından engellenir. Ve genelde herkesin karşılaştığı &quot;<em>Bu dosya bir uygulama tarafından kullanıyor.</em>&quot; gibi bir mesaj alırız. Bunun için <em>unlocker</em>, <em>process explorer </em>gibi harici programlar ile aktif kalan bu handle&#8217;lar kapatılarak dosyalar silinebilir. Bu yüzden okuma yazma işlemlerimiz biter bitmez stream nesnemizi <font
face="Courier New">Free </font>yapmalıyız.</p><p
align="justify">Kısaca bir stream nesnesini oluşturmak için aşağıdaki <strong>şablon</strong>u kullanmalıyız:</p><pre>
<pre class="brush: delphi">
var
  AStream: TBirStreamSınıfı;
begin
  AStream := TBirStreamSınıfı.Create({Stream sınıfına ait parametreler...});
  try
    //Stream üzerinde okuma yazma işlemleri
  finally
    AStream.Free;
  end;
end;
</pre></pre><h1>Stream &Uuml;zerinde Okuma-Yazma İşlemleri</h1><p
align="justify">Stream&#8217;ler üzerinde yazma işlemleri için başlıca <font
face="Courier New">Write </font>ve <font
face="Courier New">WriteBuffer </font>metodları ve okuma işlemleri için de <font
face="Courier New">Read </font>ve <font
face="Courier New">ReadBuffer </font>metodları tanımlanmıştır. Şimdi göreceğimiz işlemler <em>Win32</em>&#8216;ye göre anlatılacaktır. İleride ayrı bir başlık halinde <font
face="Courier New">TStream </font>sınıfının <em>.NET</em> üzerinde nasıl yazma okuma yapabileceğiniz de göreceğiz. Ayrıca .NET içinde bulunan <font
face="Courier New">Stream </font>sınıflarına değineceğiz.</p><p
align="justify">Stream üzerinde yapılan okuma yazma işlemleri bir <strong>işaretçi </strong>vasıtası ile yapılır. Bu işaretçi hangi pozisyonda ise o pozisyonda okuma ve yazma işlemi yapılır. İlk stream nesnesi oluşturulduğunda işaretçinin pozisyonu <strong>sıfırdır</strong>. Bu metodlar çağrıldığı vakit, yani her okuma ve yazma işleminin <strong>sonunda </strong>stream işaretçisi <strong>bir sonraki</strong> pozisyona kayar. Stream işaretçisi, stream verisinin sonuna gelip gelmediğini test etmek için <font
face="Courier New">Eof </font>metodu kullanılır.Bu paragraf streamlerin çalışma mantığının anlaşılması için önemlidir. Bu yüzden dönüp ikinci bir defa okumanızda fayda var.</p><p
align="justify">Sonunda <font
face="Courier New">Buffer </font>yazan metodların yazmayanlardan farkından söz etmek istiyorum. Tek fark, &quot;istisna&quot; (<em>Exception</em>) yakalama özelliğidir. Yani mesela <font
face="Courier New">ReadBuffer </font>metodu yine alt planda <font
face="Courier New">Read </font>metodunu çalıştırır. Fakat <font
face="Courier New">Read</font> metodu bir sorunla karşılaşır ve okuyamaz ise, <font
face="Courier New">ReadBuffer</font> metodu, <font
face="Courier New">EReadError</font> istisnasını yollar. Aynı şekilde <font
face="Courier New">WriteBuffer</font> da sorun yaşadığında <font
face="Courier New">EWriteError</font> istisnasını yollar. Böylece okuma ve yazma işlemlerinde herhangi bir hata olup olmadığının tespiti için <font
face="Courier New">ReadBuffer</font> ve <font
face="Courier New">WriteBuffer</font> ile beraber istisnaların yaklanması kafidir.</p><p
align="justify">Okuma ve yazma metodları, işlemlerini bir <strong>buffer</strong> aracılığı ile gerçekleştirir. Bu buffer bir <strong><font
face="Courier New">string</font></strong> olabilceği gibi bir <font
face="Courier New">Integer</font>, bir <strong><font
face="Courier New">record</font></strong> dahi olabilir. Bu yüzden her veri tipi için okuma ve yazma işlemlerinin nasıl olduğunu ayrı ayrı incelemeye çalışalım.</p><h2>Sayısal Değelerin Okunması ve Yazılması</h2><p
align="justify"><font
face="Courier New">Integer</font>, <font
face="Courier New">Byte</font>, <font
face="Courier New">Double</font> gibi sayısal verileri okuyup yazmak diğerlerine nazaran en basit işlemdir. Ekstra bir çevirim yapmamıza gerek kalmamaktadır.</p><p
align="justify">Bunu bir örnek üzerinde görelim. İki adet button yerleştirelim. Birinin <font
face="Courier New">Caption</font> özelliği &quot;Yaz&quot;, diğerinin <font
face="Courier New">Caption</font> özelliği de &quot;Oku&quot; olsun. &quot;Yaz&quot; butonuna çift tıklayalım ve şunları yazalım:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  BirStream: TFileStream;
  BirInteger: Integer;
  BirDouble: Double;
  BirReal: Real;
  BirByte: Byte;
begin
  BirStream := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmCreate or fmShareDenyNone);
  try
    BirInteger := 15000;
    BirDouble := 1.3;
    BirReal := 123.123123123;
    BirByte := 65;
    BirStream.WriteBuffer(BirInteger, SizeOf(Integer));
    BirStream.WriteBuffer(BirDouble, SizeOf(Double));
    BirStream.WriteBuffer(BirReal, SizeOf(Real));
    BirStream.WriteBuffer(BirByte, SizeOf(Byte));
  finally
    BirStream.Free;
  end;
end;
</pre></pre><p
align="justify">Gördüğünüz gibi bir <font
face="Courier New">TFileStream</font> nesnesi oluşturduk ve bu stream nesnesini kullanarak dosyaya bir takım sayısal değerler yazdırdık. Mesela ilk yazdırma örneğimizde <em>buffer</em>, <font
face="Courier New">Integer</font> tipinde bir değişkendir. Bu <font
face="Courier New">Integer</font> değeri yazdırırken ne kadar yazdırılması gerektiğini <font
face="Courier New">WriteBuffer</font> metodunun <strong>ikinci</strong> parametresi olarak giriyoruz. Yani yazdıracağımız bu buffer kaç byte yer tutacak, onu ikinci parametre olarak giriyoruz. Örneğimizde, bu parametre olarak <font
face="Courier New">Integer</font>&#8216;ın boyutunu giriyoruz. Bunun için <font
face="Courier New">SizeOf</font> fonksiyonundan faydalanıyoruz. Help dosyasına baktığımızda, <font
face="Courier New">Integer</font>&#8216;ın 4 byte yer kapladığını görürüz. Burada yaptığımız işlem dosyada 4 byte&#8217;lık bir alana <font
face="Courier New">Integer</font> değeri yazdırmaktan ibarettir.</p><p
align="justify">Ardından gelen yazma işlemleri de aynı mantık ile yapılmaktadır. Burada dikkat etmemiz gereken nokta ikinci parametredir. &Ccedil;ünkü bu parametre yanlış girilirse hem yazmada hem de okumada eksik ve yanlış verilerle karşılaşırız. Bu örnekten de anladığımız gibi sayısal veriler ile uğraşırken <font
face="Courier New">SizeOf(<em>DeğişkeTipi</em>)</font> şeklinde ikinci parametreyi belirlememiz yeterlidir.</p><p
align="justify">Şimdi bu programı çalıştırıp Yaz butonuna basalım. Diskin <font
face="Courier New">C:</font> bölümünde <em>deneme.bin</em> isimli bir dosya oluşmuş olmalı. Herhangi bir hex editor ile (<a
href="http://mh-nexus.de/en/" target="_blank">HxD</a>) bu dosyayı açıp inceleyebilirsiniz. Eğer ayrıntısına girmek isterseniz <font
face="Courier New">Integer</font> ve diğer sayısal değerlerin nasıl yazıldığını inceleyebilirsiniz. Mesela demiştik ki dosyamızın ilk 4 byte&#8217;ında bir <font
face="Courier New">Integer</font> bulunmaktadır. Dosyamızı bir hex editör ile açtığımızda ilk 4 byte&#8217;ında <font
face="Courier New">98 3A 00 00</font> yazdığını görürüz. IBM<em> (x86)</em> tabanlı makinelerde bu veriler tersten yazılırlar. Bu yüzden bu ifadeyi <font
face="Courier New">00 00 3A 98</font> olarak düşüneceğiz. Hesap makinesini açıp 16&#8242;lık <em>Hex</em> moda geçelim. Tabi bunu yapabilmek için bilimsel moda geçmeniz gerekmektedir. Hex modu seçili iken <font
face="Courier New">3A98</font>&#8216;i yazalım. Ardından <em>Dec</em> yani ondalık olarak her zaman kullandığınız sayı formatına çevirelim. Ve karşımızda <font
face="Courier New">15000</font> sayısını göreceğiz ki bu değer örneğimizde <font
face="Courier New">Integer</font> olarak dosyanın baş tarafına yazdırılmıştı. Diğer veri tiplerinin nasıl kaydedildiğini bu şekilde inceleyerek bulabilirsiniz.</p><p
align="justify">Şimdi gelelim bu değerleri okumaya&#8230; Oku butonumuza çift tıklayıp şu kodları girelim:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  BirInteger: Integer;
  BirDouble: Double;
  BirReal: Real;
  BirByte: Byte;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmOpenRead or fmShareDenyNone);
  try
    DosyaOku.ReadBuffer(BirInteger, SizeOf(Integer));
    DosyaOku.ReadBuffer(BirDouble, SizeOf(Double));
    DosyaOku.ReadBuffer(BirReal, SizeOf(Real));
    DosyaOku.ReadBuffer(BirByte, SizeOf(Byte));
    ShowMessage(&#039;Integer: &#039; + IntToStr(BirInteger) + #13 +
      &#039;Double: &#039; + FloatToStr(BirDouble) + #13 +
      &#039;Real: &#039; + FloatToStr(BirReal) + #13 +
      &#039;Byte: &#039; + IntToStr(BirByte)
    );
  finally
    DosyaOku.Free;
  end;
end;
</pre></pre><p
align="justify">Şimdi bu programı çalıştıralım ve bu sefer oku butonuna basalım. Ve karşımızda dosyaya yazdırdığımız değerler&#8230;</p><p
align="justify">Gördüğünüz gibi okuma işlemi yazma işleminden farklı değil. İhtiyacımız olan tek şey bir <strong>buffer</strong> ve bu buffer&#8217;ın tipinin <strong>boyutu</strong>. Eğer değişken tipinin boyutunu yanlış girersek veya okuma sırasını yer değiştirirsek, istediğimiz sonucu alamayız. &Ccedil;ünkü stream üzerine yazarken, <strong>bu sırada</strong> ve <strong>bu değişken tipleri</strong> ile yazılmıştır. Okurken de <strong>aynı sıra</strong> ve <strong>uzunlukta</strong> okunmalıdır. Bu yüzden, bu şekilde kendi dosya formatlarımızı oluştururken, bir plan üzerinde dosya formatında hangi pozisyonda hangi veri bulunacağı ve boyutu belirlenmelidir.</p><p
align="justify">Gördüğünüz gibi sayısal verileri okumak ve yazmak hiç de zor değil. Şimdi dilerseniz karakter dizilerini nasıl okuyup yazacağımızı görelim.</p><h2>Karakter Dizilerinin Okunup Yazılması</h2><p
align="justify">Karakter dizisi dediğimizde aklımıza gelen ilk değişken tipleri <font
face="Courier New">string</font> ve <font
face="Courier New">PChar</font>&#8216;dır. Eğer Delphi 2009&#8242;dan önceki bir sürüm kullanıyorsanız bu değişken tipleri <em>ANSI</em>&#8216;dir. Yani her bir karakter <strong>bir byte</strong> yer kaplar. D2009 ve sonrası için her bir karakter <em>Unicode</em> olduğu için <strong>2 byte</strong> yer kaplarlar.</p><p
align="justify">İlk önce <font
face="Courier New">PChar</font> verilerini nasıl yazıyoruz ve okuyoruz onu görelim:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Karakterler: PChar;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Karakterler := &#039;http://www.diyezon.com&#039;;
    DosyaYaz.WriteBuffer(Karakterler^, Length(Karakterler));
  finally
    DosyaYaz.Free;
  end;
end;
</pre></pre><p
align="justify">Karakter verililerini yazdırırken ve okurken dikkat etmemiz gereken nokta yine verinin <strong>kapladığı</strong> alandır. Karakter dizisinin kapladığı alanı hesaplayabilmek için <strong>bir karakterin</strong> ne kadar yer kapladığını bilmemiz gereklidir. Önceden de dediğim gibi, eğer karakterleriniz ANSI ise yani Unicode değilse, her bir karakter <strong>1 byte</strong> yer kaplar. Bu yüzden bu örneğimizde yazılacak uzunluk olarak karakter sayısını verdik. &Ccedil;ünkü ANSI karakter katarlarında karakter sayısı, karakter dizisinin boyutuna <strong>eşittir</strong>. Fakat <font
face="Courier New">PChar</font> yerine <font
face="Courier New">PWideChar</font> kullansa idik ya da bu kodları D2009 ve üzeri bir sürümde çalıştırsa idik her bir karakteri 2 byte olarak alacaktık. Bu durumda karakter sayısı karakter dizisinin boyutuna eşit değil, boyutun <strong>yarısı</strong> kadarı olacaktı. Yani Yukarıdaki kodda <font
face="Courier New">PChar</font>&#8216;ı <font
face="Courier New">PWideChar</font> yaparsak <font
face="Courier New">WriteBuffer</font> metodunun ikinci paramteresine <font
face="Courier New">Length(Karakterler) * 2</font> yazmamız gerekiyordu. Bu bahsettiğimizi örneğimizde değiştirip deneyin ve bir hex editör ile her bir karakterin 2 byte yer kapladığına şahid olun. Ama daha sonra tekrar <font
face="Courier New">PChar</font> olarak değiştirip dosyayı tekrar yazın. &Ccedil;ünkü birazdan yapacağımız okuma işleminde <font
face="Courier New">PChar</font> olarak okutacağız.</p><p
align="justify">Okuma işlemine geçmeden önce <font
face="Courier New">WriteBuffer</font> metodunda ilk parametreye bir göz atalım. Burada gördüğünüz gibi <font
face="Courier New">Karakterler</font> değişkeninin sonuna <font
face="Courier New">^</font> operatörünü ekledik. Bu operatör, işaretçilerle (<em>pointer</em>) birlikte kullanılan özel bir operatördür. Bir işaretçinin <strong>işaret ettiği esas değere</strong> erişmek için bu operatörü kullanıyoruz. Örneğimizde ise PChar tipindeki değişkenimizin işaret ettiği gerçek değeri buffer parametresi olarak girdik. &Ccedil;ünkü <font
face="Courier New">PChar</font> da bir çeşit işaretçidir. Ve <font
face="Courier New">Karakterler^</font> yazmak ile bu işaretçinin hafızada işaret ettiği gerçek karakter verisini buffer olarak girdik.</p><p
align="justify">Şimdi yazdırdığımız bu stream verilerini okuyalım:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Karakterler: PChar;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    Karakterler := AllocMem(22);
    DosyaOku.ReadBuffer(Karakterler^, 22);
    ShowMessage(Karakterler);
  finally
    DosyaOku.Free;
  end;
end;
</pre></pre><p
align="justify">İlk yaptığımız işlem <font
face="Courier New">Karakterler</font> değişkeni için hafızadan yer ayırmaktır. Bunun için <font
face="Courier New">AllocMem</font> fonksiyonu kullanılır. Tabi bunun için okuyacağımız verinin <strong>ne kadar uzunlukta</strong> olduğunu bilmemiz gerekiyor. Eğer okuyacağımız karakter verilerinin uzunluğu belli değilse ya da duruma göre boyut <strong>değişebiliyorsa</strong> bu durumda daha başka bir şeyler yapmamız gerekecek. Bununla ilgili ayrıntılı bilgiyi ilerleyen konularda bulacağız. Ama şimdilik veri uzunluğumuzu <strong>sabit</strong> kabul ederek işlemlerimizi yapalım.</p><p
align="justify"><font
face="Courier New">AllocMem</font> ile hafızadan yer ayırdıktan sonra bu hafıza bölgesine stream verilerini yerleştirebiliriz. Bunun için yine <font
face="Courier New">ReadBuffer </font>ile stream&#8217;den veri okuyoruz ve buffer&#8217;a atıyoruz. Yine buffer olarak işaretçinin gerçek değerini ifade eden <font
face="Courier New">^</font> operatörünü kullanıyoruz. İkinci parametrede de ne kadar uzunlukta okuyacağımızı belirliyoruz.</p><p
align="justify"><font
face="Courier New">PChar</font> ile veri okuma ve yazma bu şekilde olmakta. Peki <font
face="Courier New">string</font> bir değişkeni nasıl yazıp okuyacağız? Aslında <font
face="Courier New">PChar</font>&#8216;da kullandığımız yöntemden farklı değil. &Ccedil;ünkü <font
face="Courier New">string</font> ile <font
face="Courier New">PChar </font>arasında çok büyük bir fark bulunmamakta. Zaten çalışma anında bu iki tip arasında dönüşümler rahatlıkla yapılabilmektedir. Yazma için yazdığımız kodları aşağıdaki gibi değiştirelim:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Karakterler: string;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Karakterler := &#039;http://www.diyezon.com&#039;;
    DosyaYaz.WriteBuffer(Pointer(Karakterler)^, Length(Karakterler));
  finally
    DosyaYaz.Free;
  end;
end;
</pre></pre><p
align="justify">Önceki yazma işleminde çok farklı olmadığını burada görüyoruz. Tek yaptığımız işlem derleyiciye, <font
face="Courier New">string</font> değişkene  bir <strong>işaretçi</strong> gibi davranmasını söylüyoruz. Bu şekilde derleyici, <font
face="Courier New">string</font> olan <font
face="Courier New">Karakterler</font> değişkenini bir işaretçi gibi düşünecek ve <font
face="Courier New">^</font> operatörü ile esas hafıza bölgesindeki veriyi buffer olarak kullanacak. Burada <font
face="Courier New">Pointer</font> yerine <font
face="Courier New">PChar</font> yazarak da <em>type casting</em> yapabilirsiniz.</p><p
align="justify">Okuma kodları <font
face="Courier New">PChar</font>&#8216;a göre fazla ya da eksik değil. Önceki okuma fonksiyonumuzu aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Karakterler: string;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    SetLength(Karakterler, 22);
    DosyaOku.ReadBuffer(Pointer(Karakterler)^, 22);
    ShowMessage(Karakterler);
  finally
    DosyaOku.Free;
  end;
end;
</pre></pre><p
align="justify"><font
face="Courier New">AllocMem</font> yerine <font
face="Courier New">string</font> için <font
face="Courier New">SetLength</font> fonksiyonunu kullanıyoruz. Böylece string olan değişkenimizin boyutunu belirliyoruz. <font
face="Courier New">ReadBuffer</font>&#8216;da yapılanlar önceden anlatılmıştı.</p><p
align="justify">String için yaptığımız bu işlemler biraz pointer yani işaretçi bilgisi gerektiren işlerdi. Halbuki bu pointer işlerine girmeden de string verileri yazıp okuyabiliriz. Ama ilk önce bu şekilde vermemin sebebi, esasında işin arkaplanda çalışma şeklini göstermekti.</p><p
align="justify">Aslında diziler ve işaretçi olan PChar gibi karakter katarları hafızada bulunan esas verilerdeki <strong>ilk elemana</strong> işaret ederler.  Derleyici bu değişken tipleri ile uğraşırken bunların uzunluğundan haberdardır ve ilk pozisyondan itibaren hafızada bu değişkenler üzerinde işlem yapabilir. Esasında <font
face="Courier New">PChar</font> ve <font
face="Courier New">string</font>&#8216;de ortak olarak yapılan işlem, karakter dizisinin <strong>ilk elemanını</strong> buffer olarak girmektir. Her iki durumda da <font
face="Courier New">^</font> operatörü dizinin hafızadaki <strong>ilk karakterine</strong> işaret eder. Geri kalanında <font
face="Courier New">Write</font> ya da <font
face="Courier New">Read</font> metodları verilen uzunluk kadar bu ilk karakterden itibaren okumaya ya da yazmaya başlar. Yukarıda <font
face="Courier New">string</font> için yazdığımız kodları şu şekilde daha kolay ve sade bir şekilde de yazabiliriz:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Karakterler: string;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    SetLength(Karakterler, 22);
    DosyaOku.ReadBuffer(Karakterler[1], 22);
    ShowMessage(Karakterler);
  finally
    DosyaOku.Free;
  end;
end;

procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Karakterler: string;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Karakterler := &#039;http://www.diyezon.com&#039;;
    DosyaYaz.WriteBuffer(Karakterler[1], Length(Karakterler));
  finally
    DosyaYaz.Free;
  end;
end;
</pre></pre><p
align="justify">Gördüğünüz gibi pointer&#8217;lardan kurtulup, bize yakın olan kodları kullandık ve aynı sonucu aldık. Ama arka planda derleyici bizim ilk yazdığımız ve işaretçileri kullandığımız kısımdaki gibi çalışır.</p><p
align="justify">Burada tek yaptığımız işlem buffer olarak <font
face="Courier New">string</font> değişkeninin <strong>ilk karakterini</strong> girmek. Tabi okuma işleminde yine <font
face="Courier New">SetLength</font>&#8216;i unutmuyoruz. &Ccedil;ünkü okuma işleminden önce hafızada yeterince yer ayrılması şarttır.</p><p
align="justify">Önceden de dediğim gibi uzunluğu sabit uzunlukta olmayan karakter dizileri için ayrı bir konu başlığı ayırdık. O zamana kadar karakter dizimizin boyutunu sabit kabul edeceğiz.</p><h2>Record&#8217;ların Stream Verileri Olarak Kullanılması</h2><p
align="justify">Genelde stream ile yapılmak istenen bir gurup veriyi yazmak ve okumaktır. Bu gurup verileri ise genelde <strong><font
face="Courier New">record</font></strong>&#8216;lar ile tutarız. Mesela aşağıda bulunan record içinde bir çok değişik değişken tipi bulunmaktadır.</p><pre>
<pre class="brush: delphi">
TDosyaBasligi = record
  Imza: string[3];
  Versiyon: Double;
  IcerikBoyutu: Integer;
end;
</pre></pre><p
align="justify">Record&#8217;ları streamler ile beraber kullanırken şuna dikkat etmemiz gerekiyor. Bir <font
face="Courier New">record</font> verisini stream ile okuyup yazabilmek için, record içeriğinin <strong>sabit uzunluklu</strong> verilerden oluşuyor olmasıdır. Aksi halde bu record, özel işlemler yapılmadığı taktirde, streamlerde kullanılmak için elverişli değildir. Bu yüzden yukarıda <font
face="Courier New">string</font> değişkenini 3 karakter ile sınırlandırdık.</p><p
align="justify">Okuma esnasında da aynı record verisini kullanarak rahat bir şekilde okuma işlemini yapabilriiz. Peki ama bu bize ne avantaj sağlayacak?</p><p
align="justify">Önceki örneklerde gördüğümüz gibi, herbir <font
face="Courier New">string</font>, <font
face="Courier New">Integer</font>, <font
face="Courier New">Double</font> gibi değişkenler için ayrı ayrı <font
face="Courier New">Read</font> ve <font
face="Courier New">Write</font> metodları kullandık. Halbuki bu değişkenler eğer sabit bir formda ise bunları bir paket halinde okuyup yazabilmeliyiz. İşte bu paket halinde okuyup yazma işlemi için recordları kullanıyoruz. Yani yukarıda verdiğimiz <font
face="Courier New">TDosyaBasligi</font> isimli recordu yazabilmek için tek tek içinde bulunan 3 değişkeni yazdırmamız gerekmiyor. Sadece <font
face="Courier New">TDosyaBasligi</font> isimli recordu yazdırmamız kafidir.</p><p
align="justify">Şimdi yukarıda verdiğmiz recordu <font
face="Courier New"><strong>interface</strong></font> kısmında, <font
face="Courier New">TForm</font> tanımlamasının yukarısında ama <strong><font
face="Courier New">type</font></strong>&#8216;ın altında yazalım. Ve Yaz butonunun <font
face="Courier New">OnClick</font> olayına şunları yazalım:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnYazClick(Sender: TObject);
var
  DosyaYaz: TFileStream;
  Paket: TDosyaBasligi;
begin
  DosyaYaz := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmCreate or fmShareDenyNone);
  try
    Paket.Imza := &#039;DYZ&#039;;
    Paket.Versiyon := 1.2;
    Paket.IcerikBoyutu := 1500;
    DosyaYaz.WriteBuffer(Paket, SizeOf(TDosyaBasligi));
  finally
    DosyaYaz.Free;
  end;
end;
</pre></pre><p
align="justify">Gördüğünüz gibi herhangi bir ekstra işlem yapmadım. Sanki basit bir sayısal veriyi yazdırır gibi recordumuzu yazdırdık. Aynı şekilde okumasını da gerçekleştirelim:</p><pre>
<pre class="brush: delphi">
procedure TForm1.btnOkuClick(Sender: TObject);
var
  DosyaOku: TFileStream;
  Paket: TDosyaBasligi;
begin
  DosyaOku := TFileStream.Create(&#039;c:\deneme.txt&#039;, fmOpenRead or fmShareDenyNone);
  try
    DosyaOku.ReadBuffer(Paket, SizeOf(TDosyaBasligi));
    ShowMessage(Paket.Imza + #13 + FloatToStr(Paket.Versiyon) + #13 +
      IntToStr(Paket.IcerikBoyutu));
  finally
    DosyaOku.Free;
  end;
end;
</pre></pre><p
align="justify">Bu programı çalıştıralım ve ilk önce Yaz sonra da Oku buttonlarına tıklayalım. Böylece bir recordu yazıp okuyabildik. Fakat disk üzerine yazılan dosyayı bir hex editör yardımı ile incelersek göreceğiz ki ilk kısımda bulunan 3 karakterlik stringten <strong>önce 1 byte&#8217;lık</strong> bir veri bulunmaktadır. Önceki string yazma işlemlerinden farklı olarak burada record içindeki stringlerin sadece verisi yazdırılmaz. <font
face="Courier New">String</font> değişkeninin <strong>tamamı</strong> record ile beraber yazdırılır. <font
face="Courier New">String</font> değişkenin tamamından kastımız stringi oluşturan parçalardır. Bir <font
face="Courier New">string</font>, <font
face="Courier New">PChar</font> gibi karakter dizilerinden farklı olarak ilk <font
face="Courier New">0</font>. baytında stringin veri uzunluğunu tutar. Bu örneğimizde 3 karakter uzunluğunda olduğu için hex editörde ilk göreceğimiz değer <font
face="Courier New">03</font> değeridir. Tabi bu tek bytelık alanda en fazla 255 değeri yani <font
face="Courier New">FF</font> değerini tutlabilir. Bu yüzden sınırlı bir <font
face="Courier New">string</font>(<em>literal string</em>) kullanabilmek için en fazla 255 karakter kullanabiliriz. Aksi halde daha başka yollara başvurmalıyız. Başta bulunan <font
face="Courier New">03</font> ifadesini kaldırıp sadece string verisini yazdırmak istersek <strong><font
face="Courier New">array</font></strong> kullanmamız gerekmektedir. <font
face="Courier New">Array</font> kullanımını bir sonraki konuda göreceğiz.</p><p
align="justify">Sabit uzunluklu recordları kaydederken gördüğünüz gibi hiç zorlanmıyoruz. Eğer record içeriği burada olduğu gibi sabit uzunluklu değilse yani record içine <font
face="Courier New">PChar</font> ya da limitsiz <font
face="Courier New">string</font> girersek ne yapacağız. Bu durumda eğer yapabiliyorsak değişken uzunluklu olanları recorddan <font
face="Courier New">çıkartıp</font> işlemlerimizi o şekilde yapmalıyız. Değişken uzunluklu verileri recorddan <strong>ayrı olarak</strong> okuyup yazmamız gerekmektedir. İleriki konularımızda değişken uzunluklu veriler ile nasıl çalışacağımızı da göreceğiz.</p><h2>Dizileri Stream Verisi Olarak Kullanmak</h2><p
align="justify">Aslında dizileri stream üzerine yazmak için, buraya kadar öğrendiğimiz bilgiler yeterli. String&#8217;leri nasıl yazdırır ve okutuyorsak, aynı şekilde array&#8217;leri de yazdırıp okutabiliyoruz.</p><pre>
<pre class="brush: delphi">
//Yazdırmak için
var
  FS: TFileStream;
  BirDizi: array[0..5] of Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmCreate or fmShareDenyNone);
  try
    BirDizi[0] := 153;
    BirDizi[1] := 1;
    BirDizi[2] := 3442;
    BirDizi[3] := 902;
    BirDizi[4] := 455;
    FS.WriteBuffer(BirDizi[0], SizeOf(BirDizi));
  finally
    FS.Free;
  end;
end;

//Okutmak için
var
  FS: TFileStream;
  BirDizi: array[0..5] of Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.bin&#039;, fmOpenRead or fmShareDenyNone);
  try
    FS.ReadBuffer(BirDizi[0], SizeOf(BirDizi));
    ShowMessage(IntToStr(BirDizi[0]) + #13 +
      IntToStr(BirDizi[1]) + #13 +
      IntToStr(BirDizi[2]) + #13 +
      IntToStr(BirDizi[3]) + #13 +
      IntToStr(BirDizi[4]) + #13);
  finally
    FS.Free;
  end;
end;
</pre></pre><p
align="justify">Gördüğünüz gibi <strong>ilk elemanını</strong> buffer olarak giriyoruz ve ikinci parametre olarak da <strong>dizinin boyutunu</strong> parametre olarak giriyoruz.</p><p
align="justify">Eğer <strong>dinamik</strong> array kullanıyorsak okuma işlemini gerçekleştiren <font
face="Courier New">ReadBuffer</font> metodundan önce mutlaka <font
face="Courier New">SetLength</font> ile dizinin boyutunu belirlemiş olmalıyız. Ayrıca dizinin kapladığı alanı bulabilmek için dinamik dizinin eleman sayısı ile elaman tipinin boyutunu çarpmalıyız. Yani yukarıdaki örnekte dinamik dizi kullanmış olsa idik <font
face="Courier New">Length(BirDizi) * SizeOf(Integer)</font> kazacaktık.</p><p
align="justify">İki ve daha fazla boyuttaki diziler için ise, iç içe bir <strong><font
face="Courier New">for</font></strong> döngüsü kurularak bir okuma ve yazma algoritması geliştirilebilir.</p><p
align="justify">Bu kısım diziler için kolay kısımdı. &Ccedil;ünkü bunlar önceki öğrendiklermizden farksızdır. Fakat eğer ActiveX ile uğraşıyorsanız, Word, Excel gibi programların otomosyonu ile uğraşıyorsanız <strong>variant array</strong>&#8216;ler ile uğraşmışsınızdır. İşte bu durumda variant olan array&#8217;leri stream&#8217;e kaydetmek ve stream&#8217;den okutmak bu kadar kolay değildir. Ama zor da değildir. Eğer ActiveX ile uğraşmıyorsanız diğer başlığa atlayabilirsiniz, ya da nasıl yapıldığı hakkında fikir sahibi olmak için kalıp okuyabilirsiniz.</p><p
align="justify">Bu örneğimizde otomosyon ya da ActiveX nesnelerine girmeyeceğim. Bir şekilde bu nesnelerden variant array verilerini aldığınızı düşünerek haraket edeceğim.</p><pre>
<pre class="brush: delphi">
var
  VariantArray: Variant;
  VArraySize: Integer;
  PArray: Pointer;
  FS: TFileStream;
begin
  VariantArray := ActiveXNesnesi.ArrayDonderenMetod; //Bu kısım size ait...
  VArraySize := VarArrayHighBound(VariantArray, 1) - VarArrayLowBound(VariantArray, 1) + 1;
  PArray := VarArrayLock(VaraintArray);
  FS := TFileStream.Create;
  try
    FS.WriteBuffer(PArray^, VArraySize);
  finally
    FS.Free;
  free;
  VarArrayUnlock(VaraintArray);
</pre></pre><p
align="justify">Karışık gibi görünse de eğer ActiveX ile uğraşıyorsanız bu kısım size zor gelmeyecektir. İlk başta bir nesneden variant array değerini aldık. Bu değeri siz delphi içinde de oluşturabilirsiniz. Ardından bu array&#8217;in boyutunu hesapladık ve <font
face="Courier New">VArraySize</font> isimli değişkene attık. Burada dikkat etmeniz gereken nokta, bu örneğimizde diziyi byte, char gibi her elemanı 1 byte olan bir diziye göre hesapladık. Eğer array&#8217;deki elemanların boyutu 1 byte&#8217;dan fazla ise bu durumda:</p><pre>
<pre class="brush: delphi">
VArraySize := (VarArrayHighBound(VariantArray, 1) -
  VarArrayLowBound(VariantArray, 1) + 1) * TVarData(VariantArray).VArray^.ElementSize;
</pre></pre><p
align="justify">gibi bir şeyler yazmamız gerekiyordu. Yani eleman sayısı ile bir elemanın boyutunu çarpıyoruz. Daha sonraki gelen işlemlerde, variant array&#8217;in <strong>ilk elemanının</strong> işaretçisini alıyoruz. Bunun için <font
face="Courier New">VarArrayLock</font> fonksiyonundan faydalanıyoruz. Bu işaretçiyi daha sonra buffer olarak <font
face="Courier New">WriteBuffer</font>&#8216;da kullanıyoruz. Bu işaretçi ile işimiz bitince <font
face="Courier New">VarArrayUnlock</font> yapmayı unutmuyoruz.</p><p
align="justify">Variant dizisini stream&#8217;den okurken de <font
face="Courier New">VarArrayCreate</font> fonksiyonunu kullanıyoruz ve yeni bir dizi oluşturuyoruz. Ardından aynı <font
face="Courier New">VarArrayLock</font> ve <font
face="Courier New">VarArrayUnlock</font> işlemleri arasında dizimizi stream&#8217;den okuyoruz. Bu kısmı size bırakıyorum&#8230;</p><h2>Değişken Uzunluktaki Verileri Okuyup Yazmak</h2><p
align="justify">Buraya kadar öğrendiğimiz işlemlerde hep varsayılan bir uzunluktan bahsettik. Fakat her zaman durum bu şekilde olmuyor.</p><p
align="justify">Mesela, kullanıcının bir <font
face="Courier New">TMemo</font> bileşenini doldurduğu bir form var. Biz bu <font
face="Courier New">TMemo</font>&#8216;da bulunan yazıyı ve başka verileri kendi dosya formatımızda saklamak istiyoruz. Ya da veritabanına kendi formatımızda kaydetmek istiyoruz. Bu durumda string&#8217;in ne uzunlukta olduğunu <strong>yazarken</strong> bilebiliriz fakat <strong>okurken bilemeyiz</strong>. &Ccedil;ünkü dosya&#8217;da ya da herhangi bir stream&#8217;de kayıtlı bulunan o string&#8217;e ait <strong>uzunluk</strong>, artık bizim bilgimizin <strong>dışına</strong> çıkmıştır. Buraya kadar yaptığımız örneklerde hep yazma ve okuma işlemlerini aynı programa koyduk ve aynı çalışma zamanında işlettirdik. Bu yüzden okuduğumuz verilerin boyutunu her zaman biliyorduk. Halbuki okuma fonksiyonu ayrı bir işlemde ise ya da programı açıp kapadığımızda da okuma işleminin çalışmasını istiyorsak bu durumda kaydedeceğimiz veriyi özel bir biçimde kaydetmeliyiz.</p><p
align="justify">Bu ve bunun gibi durumlarda yapacağımız işlem çok basit ve her türlü <strong>değişken uzunluklu</strong> veri tipi için geçerlidir. Yapacağımız tek şey değişken uzunluklu olan verinin boyutunu <strong>sabit</strong> bir pozisyona yazmaktır. Ardından okuma esnasında bu sabit pozisyondaki değeri <strong>okutarak</strong> değişken olan verinin boyutu hakkında bilgi sahibi olabiliriz.</p><p>Mesela stream formatımız şu şekilde olsun:</p></p><table
height="94" width="368" cellspacing="1" cellpadding="1" border="1" align="" summary=""><tbody><tr><td><p><strong>Pozisyon</strong></p></td><td><p><strong>Boyut</strong></p></td><td><p><strong>Değişken Tipi</strong></p></td><td><p><strong>Açıklama</strong></p></td></tr><tr><td>0</td><td>3 byte</td><td>String[3]</td><td>Dosya İmzası</td></tr><tr><td>3</td><td>8 byte</td><td>Double</td><td>Versiyonu</td></tr><tr><td>15</td><td>4 byte</td><td>Integer</td><td>İçerik Boyutu</td></tr><tr><td>19</td><td>Değişken</td><td>String</td><td>İçerik</td></tr><tr><td>Bilinmeyen</td><td>4 byte</td><td>Integer</td><td>Rastgele</td></tr></tbody></table><p
align="justify">Tabi bu çok basit bir format şekli. Normalde bir çok sabit içerikle beraber bir çok değişken içeriği iç içe kullanmamız gerekebiliyor.</p><p
align="justify">Formatımıza göre ilk 3 byte&#8217;da dosya imzası bulunacak. Ardından gelen 8 byte dosyanın versiyonunu tutacak. Ve sonrasında gelen 4 byte&#8217;lık Integer değer ise bizim üzerinde uğraştığımız esas konudur. &Ccedil;ünkü bu Integer değer sonrasında gelen string içeriğin boyutunu tutmaktadır. Böylece okuma esnasında string verimizi ne kadar okuyacağımızı belirleyebiliyoruz.</p><p
align="justify">En sonda ise 4 byte&#8217;lık bir <font
face="Courier New">Integer</font> daha ekledim. Bu değer aslında bu örneğimizi için gerekli değil. Fakat burada bir noktaya dikkatinizi çekmek istediğimden bu değeri de dosya formatına ekledim.</p><p
align="justify">Değişken uzunlukta olan <font
face="Courier New">string</font>, <font
face="Courier New">array</font> ya da herhangi bir değişkeni kaydederken normalde ilk akla gelen stream&#8217;in sonuna kadar okumak olacaktır. Ya da stream&#8217;in uzunluğundan diğer verilerin boytunu çıkarmak olacaktır. Halbuki işin içine iki veya daha fazla değişken uzunlukta veri girdiğinde ve buradaki örnekte olduğu gibi değişken uzunluktan sonra da bir başka veri geldiğinde bu mantık bizi idare etmeyecektir. Aynı şekilde dosya formatımızı sürekli olarak güncellenebilen bir format ise bu mantık yine çökecektir.</p><p
align="justify">Bu durumda en mantıklı işlem, değişken verilerin <strong>başında</strong> bu verinin uzunluğunu <strong>saklamak</strong> olacaktır. Böylece sonrasında gelen değişken uzunluktaki veri rahatlıkla okunabilir.</p><p
align="justify">Şimdi bu anlattıklarımızı pratiğe dökelim ve yukarıdaki stream formatımızı yine bir dosya üzerinde yazıp okuyalım. Bunun için yeni bir uygulama açın ve form üzerine bir adet button ve bir adet memo yerleştirin.</p><p
align="justify">Unit&#8217;in <strong><font
face="Courier New">interface</font></strong> kısmında <font
face="Courier New">TForm1</font> tanımlamasının hemen üstünde(type bloğu içinde) şunları yazalım:</p><pre>
<pre class="brush: delphi">
type
  TDosyaBasligi = record
    Imza: array[0..2] of Char;
    Versiyon: Double;
    IcerikBoyutu: Integer;
  end;
</pre></pre><p
align="justify">Buttonnun <font
face="Courier New">OnClick</font> olayına aşağıdakileri yazalım.</p><pre>
<pre class="brush: delphi">
var
  FS: TFileStream;
  Baslik: TDosyaBasligi;
  Icerik: string;
  Rastgele: Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.dyz&#039;, fmCreate or fmShareDenyNone);
  try
    Icerik := Memo1.Lines.Text;
    //Başlık bilgilerinin yazımı
    Baslik.Imza := &#039;DYZ&#039;;
    Baslik.Versiyon := 1.2;
    Baslik.IcerikBoyutu := Length(Icerik);
    FS.WriteBuffer(Baslik, SizeOf(TDosyaBasligi));

    //Değişken boyutlu içeriğin yazımı
    FS.WriteBuffer(Icerik[1], Length(Icerik)); //Unicode olduğunda Length * 2

    Randomize;
    Rastgele := Random(100);
    FS.WriteBuffer(Rastgele, SizeOf(Integer));
  finally
    FS.Free;
  end;
</pre></pre></p><p
align="justify">Burada aslında yeni bir şeyler görmedik. Sadece öğrendiklerimizi bir problemin çözümünde kullandık.</p><p
align="justify"><font
face="Courier New">Baslik</font> yazdırılmadan önce, değişken uzunluktaki içeriğimiz olan memo&#8217;nun <font
face="Courier New">Text</font> bilgisinin uzunluğunu, <font
face="Courier New">Baslik</font> record verisi içine alıyoruz. Ardından bu recordu stream&#8217;e yazdıktan sonra, değişken içeriğin uzunluğu da yazılmış oldu. Ardından normal bir stringi yazdırır gibi bütün karakterlerini stream üzerine yazdırdık. Ardından da rastgele bir sayıyı bu değişken uzunluktaki veriden sonra yazdırdık. Artık programı çalıştırıp Memo içine bir şeyler yazalım ve button&#8217;a basıp verileri stream üzerine kaydedilim.</p><p
align="justify">Şimdi gelelim bu stream formatının okunmasına&#8230; Bunun için ayrı yeni bir uygulama açalım ve bir button ekleyelim. Ve bu buttonun <font
face="Courier New">OnClick</font> olayına geçip şunları yazalım:</p><pre>
<pre class="brush: delphi">
var
  FS: TFileStream;
  Baslik: TDosyaBasligi;
  Icerik: string;
  Rastgele: Integer;
begin
  FS := TFileStream.Create(&#039;c:\deneme.dyz&#039;, fmOpenRead or fmShareDenyNone);
  try
    //Başlık bilgilerinin okunması
    FS.ReadBuffer(Baslik, SizeOf(TDosyaBasligi));

    if Baslik.Imza &lt;&gt; &#039;DYZ&#039; then
    begin
      ShowMessage(&#039;Desteklenmeyen format&#039;);
      Exit;
    end;

    if Baslik.Versiyon &gt; 1.2 then
    begin
      ShowMessage(&#039;Bu format ancak üst versiyonlarda açılabilir.&#039;);
      Exit;
    end;

    //Değişken boyutlu içeriğin okunması
    SetLength(Icerik, Baslik.IcerikBoyutu);
    FS.ReadBuffer(Icerik[1], Baslik.IcerikBoyutu);
    FS.ReadBuffer(Rastgele, SizeOf(Integer));

    ShowMessage(Icerik + #13 + IntToStr(Rastgele));
  finally
    FS.Free;
  end;
end;
</pre></pre><p
align="justify">Burada yaptığımız işlem sadece verilerin okunmasından ibaret. Sadece dikkat etmemiz gereken kısım, <font
face="Courier New">ReadBuffer</font>&#8216; dan önce <font
face="Courier New">SetLength</font> ile değişken uzunlukta olan verinin uzunluğunu belirlememiz gerekiyor.</p><p
align="justify">Değişken uzunluktaki stringler bu şekilde okutulabilir.</p><p
align="justify">Bir küçük tüyo olarak, verileri yazdırdığımız kısımdaki şu kodlara bir daha göz atmanızı isteyeceğim:</p><pre>
<pre class="brush: delphi">
  Icerik := Memo1.Lines.Text;
  ......
  ......
  Baslik.IcerikBoyutu := Length(Icerik);
</pre></pre><p
align="justify">Bu kodlar <font
face="Courier New">Baslik</font> recordunun yazılmasından evvel doldurulmaktadır. Bazen öyle durumlar oluyorki, veri uzunluğunu verinin stream&#8217;e <strong>tamamen yazdırılmasına</strong> kadar <strong>bilemiyoruz</strong>. Mesela değişken uzunlukta bu stringimiz, stream&#8217;e yazdırılma esnasında belli bir algoritma ile <strong>dinamik</strong> olarak oluşturuluyor olabilir. Bu durumda string verisi tamamen stream üzerine yazdırılmadan, uzunluğunu bilemeyiz. Bu durumda <font
face="Courier New">TStream</font>&#8216;e ait <font
face="Courier New">Seek</font> metodunu kullanacağız. Bu metod stream üzerinde belli bir <strong>pozisyona atlamamızı</strong> sağlamaktadır. Bu durumda kodumuz şöyle bir şey olacaktı:</p><pre>
<pre class="brush: delphi">
  //Şimdilik yer ayırmak için 0 yazdırıyoruz.
  Baslik.IcerikBoyutu := 0;
  FS.WriteBuffer(Baslik, SizeOf(TDosyaBasligi));

  .....
  .....
  //Stream üzerine verilerin yazdırılması ve boyutunun hesaplanması...
  //Burada değişken uzunluktaki veri yazdırılmakla birlikte boyutu da hesaplanıyor.
  .....
  .....

  FS.Seek(15, soFromBeginning);
  FS.WriteBuffer(IcerikBoyutu, SizeOf(Integer));
</pre></pre><p
align="justify">Aynı şeyi <font
face="Courier New">Baslik</font> recordunu en sonda yazdırmakla da yapabilrdik. Veya daha başka yollar ile de bu dediklerimiz yapılabilir. Burada bilmemiz gereken <font
face="Courier New">Seek</font> metodunun kullanımıdır. <font
face="Courier New">Seek</font> metodunda ilk parametre olarak <font
face="Courier New">15</font> girdik. &Ccedil;ünkü örneğimizdeki stream format tablomuza baktığımızda 15. byte pozisyonunda içerik boyutu bulunmaktadır. <font
face="Courier New">Seek</font> ile stream&#8217;in <strong>yazma işaretçisini</strong> buraya taşıdık ve bu pozisyondan sonra 4 byte&#8217;lık bir <font
face="Courier New">Integer</font> değer yazdırdık. <font
face="Courier New">Seek</font> metodunda kullanılan ikinci parametre için help dosyalarını gezebilirsiniz.</p><p
align="justify">Değişken uzunlukta olan diziler için, yani dinamik diziler için de aynı mantığı kullanmamız gerekmektedir. Değişken olan dizinin boyutunu da stream üzerine yazdırmamız ve okuma anında bu değeri okuyup diziyi <font
face="Courier New">SetLength</font> ile tekrar oluşturmamız gerekmektedir. Yapacağımız işlemler aynı olacağı için bu kısmı size bırakıyorum.</p><p
align="justify">Bu bölümde bu kadar bilgi yeterli. Gelecek bölümde stream verilerinin başka streamler üzerine aktarılması ve kopyalanması, streamler ile nesne ve bileşen kullanımı, hafıza, resource, veritabanı gibi ortamlarla stream kullanımı ve son olarak .NET ortamında stream kullanımından bahsedeceğiz. İkinci bölüme geçmeden burada anlatılanları iyi bir şekilde kavrayıp, kendi denemelerinizi yapmanız gerekmektedir.</p><p
align="justify">Eleştiri ve yorumlarınızı bekliyorum&#8230;</p><p
align="justify">Fatih Tolga Ata &#8211; 2008</p></p></p></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/streamde-uzmanlasalim-bolum-1/feed/</wfw:commentRss> <slash:comments>11</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using memcached
Page Caching using memcached
Database Caching 1/13 queries in 0.021 seconds using memcached
Object Caching 373/403 objects using memcached

Served from: www.diyezon.com @ 2012-02-08 11:37:18 -->
