C# FileStream

FileStream, Stream ana soyut sınıfı kullanılarak genişletilmiş, belirtilen kaynak dosyalar üzerinde okuma/yazma/atlama gibi operasyonları yapmamıza yardımcı olan bir sınıftır. Bu sınıf sayesinde senkron ve asenkron olarak okuma/yazma operasyonlarını gerçekleştirebiliriz.

Örnek bir FileStream tanımı yapacak olursak :

FileStream fStream = File.OpenWrite("dosya.txt");

FileStream objesini dosya adını File.OpenWrite() metoduna parametre vererek oluşturabilirsiniz, böylelikle FileStream objesi FileAccess.Write erişim yetkisi ile açılmış olur. Burada dikkat edilmesi gereken nokta File.OpenWrite() metodu ile açılan dosya yok ise oluşturulacaktır. Ayrıca File.OpenWrite() metodu FileAccess.Write erişim yetkisi dışında yetki ile kullanıldığında NotSupportedException hatası alınır.

FileStream fStream = File.OpenRead("dosya.txt");

FileStream objesini dosya adını File.OpenRead() metoduna parametre vererek oluşturabilirsiniz. Dikkat edileceği üzere bu metod belirtilen dosyayı okumak için açacaktır eğer dosya yok ise FileNotFoundException hatası alınacaktır. Ayrıca bu metod ile oluşturulmuş FileStream objesi sadece okuma operasyonu için açıldığından yazma operasyonu yapamazsınız buna rağmen yazma operasyonu yaparsanız yine FileNotFoundException hatası alırsınız.

FileStream sınıfı yükleyicisi (constructor)’nin farklı bir kullanımı :

FileStream fStream = new FileStream("dosya.txt",FileMode.OpenOrCreate,
                                                  FileAccess.Write,
                                                  FileShare.None);


Yukarıdaki gibi bir kullanımda FileStream sınıfı yükleyicisi parametre olarak dosya adı, FileMode enumeration değeri, FileAccess enumeration değeri ve FileShare enumeration değeri almaktadır. FileStream objesinde kullanılan FileMode, FileAccess ve FileShare enumeration’ların ne durumlarda kullanıldığını açıklayalım.

Enumeration’lar

FileStream sınıfının yükleyicisinde kullanılan enumeration’lar aşağıdaki gibidir. İlk önce FileMode enumeration’ından başlayacak olursak :

FileMode Enumeration Değerleri

FileMode EnumerationAçıklama
CreateFileMode.Create seçeneği ile yeni bir dosya oluşturulur, aynı dosya varsa üzerine yazılır.
CreateNew Yeni bir dosya oluşturulur, eğer aynı dosya var ise IOException hatası alınır.
Append Dosya açılarak sonundan başlayarak yazma işlemi yapılır. Eğer dosya yok ise oluşturulur. FileMode.Append seçeneği sadece FileAccess.Write seçeneği ile kullanılır diğer kullanımlarda ArgumentException hatası alınır.
OpenDosya belirtilen dizinde var ise açılır. Eğer dosya yoksa FileNotFoundException hatası alınır.
OpenOrCreateDosya belirtilen dizinde var ise açılır yok ise oluşturulur.
TruncateDosya açılır ve içerisi boşaltılıp boyutu 0 byte olarak güncellenir.

FileAccess Enumeration Değerleri

FileAccess Enumeration Açıklama
ReadDosyaya sadece okuma erişim yetkisi verir.
WriteDosyaya sadece yazma erişim yetkisi verir.
ReadWriteDosyaya okuma/yazma erişim yetkisi verir.

FileShare Enumeration Değerleri

FileShare enumeration’ı kullanılan kaynak dosyanın diğer strem’ler tarafından nasıl paylaşılacağını belirten enumeration’dır.

FileShare Enumeration Açıklama
Noneİşlem yaptığınız stream dışındaki başka bir stream dosyaya okuma yada yazma için erişim sağlayamaz.
Readİşlem yaptığınız yada başka stream'ler dosyaya okuma için erişim sağlayabilirler.
Writeİşlem yaptığınız yada başka stream'ler dosyaya yazma için erişim sağlayabilirler.
ReadWrite İşlem yaptığınız yada başka stream'ler dosyaya okuma ve yazma için erişim sağlayabilirler.

FileStream metodlarını kullanarak dosyaya yazmak

FileStream metodlarını kullanmak aşağıdaki örnekte görüldüğü üzere çok basittir. Bu örnekte Write() ve WriteByte() metodlarını kullanarak dosyaya yazma işlemi gerçekleştirilmiştir.

using System;
using System.IO;

namespace StreamTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                FileStream fs = File.Open("dosya.txt", FileMode.OpenOrCreate);
                Console.WriteLine("dosya.txt yi kullanan FileStream oluşturuldu :");
                Console.WriteLine("Okuyabilir mi (CanRead)? {0}", fs.CanRead);
                Console.WriteLine("Yazabilir mi (CanWrite)? {0}", fs.CanWrite);
                Console.WriteLine("Atlayabilir mi (CanSeek)? {0}", fs.CanSeek);
                Console.WriteLine("Byte bilgiler yazılmadan önceki aktif pozisyon : {0}",
                                                                            fs.Position);
                if (fs.CanWrite)
                {
                    fs.WriteByte(65);
                    byte[] bytes = new byte[3] { 66, 67, 68 };
                    fs.Write(bytes, 0, bytes.Length);
                    Console.WriteLine("Byte bilgiler dosyaya yazıldı.");
                }
                Console.WriteLine("Byte bilgiler yazıldıktan sonraki aktif pozisyon: {0}",
                                                                             fs.Position);
                fs.Close();
                Console.ReadLine();
            }
            catch (IOException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

Yukarıdaki kod derlenip çalıştırıldığında çıktısı aşağıdaki gibi olacaktır :

FileStream çıktısı

Aşağıdaki resimdede görüleceği üzere dosya oluşturulmuş ve byte değerler içerisine yazılmıştır.

FileStream çıktısı

Şimdi kodu adım adım inceleyelim. İlk Main metodu içerisinde try bloğunda FileStream instance’ını File.Open() metoduna iki parametre vererek ouşturuyoruz. İlk parametre dosyanın bulunduğu dizin, diğer parametre ise dosyaya erişim modu enumeration değeridir. İkinci parametre FileAccess.OpenOrCreate verildiği için dosya yoksada oluşturulacaktır.

Devam eden diğer üç satırda CanRead, CanWrite ve CanSeek özelliklerinin değerlerine bakılmıştır. Bu değerlere göre stream objesinin kabiliyetlerine göre yazma, okuma ve atlama operasyonlarının hangilerinin desteklendiği anlışılabilmektedir. Örneğin NetworkStream objesi ile çalıştığınızda CanSeek özelliği false değerini dönecektir böylece atlama operasyonu yapamayacağınızı biliyor olacaksınız ve işlemlerinizi buna göre gerçekleştireceksiniz.

Bir sonraki satırda FileStream.Position değeri ekrana yazılmaktadır, buda stream’in şu anki pozisyonudur. Bu değerde şu an 0’dır çünkü stream şimdi oluşturulmuş ve herhangi bir operasyon gerçekleştirilmemiştir. Sonraki satırda CanWrite özelliği kontrol edilmiştir buradada beklendiği üzere true değeri dönmektedir. Bu değeri kontrol etmemizin sebebi FileStream.WriteByte() metodunu kullanıyor olmamızdır. Eğer yazma operasyonunu desteklemeyen bir streamde bu metodu kullansaydık hata mesajı alacaktık.

Devam eden sonraki satırda (66, 67, 68) değerlerini içeren byte dizisi oluşturuluyor ve bu byte dizi üç parametre alan FileStream.Write() metodu ile yazılmaktadır. İlk parametre stream’e yazılacak byte dizidir, ikinci parametre ise dizinin başlangıç indeksidir son parametre ise ne uzunlukta bir veri yazılacağını belirtir.

if ifadesinden sonra FileStream.Position değeri 4 olarak ekrana yazılır çünkü stream’e 4 byte’lık veri yazdık. Sonrasında ise FileStream.Close() metodunu, tampona alınmış veriler varsa temizlenmesi yada stream için ayrılan sistem kaynaklarının serbest bırakılması için çalıştırıyoruz.

FileStream metodlarını kullanarak dosyadan okuma yapmak

Aşağıdaki örnekte yukarıda oluşturduğumuz dosyayı stream ile okuyup detayları inceleyeceğiz.

using System;
using System.IO;

namespace StreamTest
{
    class Program
    {
        static void Main(string[] args)
        {
          try
          {
            using(FileStream fs = File.OpenRead("dosya.txt"))
            {
              Console.WriteLine("Stream özellikleri kontrol ediliyor :");
              Console.WriteLine("Okuyabilir mi (CanRead)? {0}", fs.CanRead);
              Console.WriteLine("Yazabilir mi (CanWrite)? {0}", fs.CanWrite);
              Console.WriteLine("Atlayabilir mi (CanSeek)? {0}", fs.CanSeek);
              Console.WriteLine("Byte bilgiler okunmadan önceki aktif pozisyon : {0}",
                                                                         fs.Position);
              if(fs.CanRead)
              {
                // dosya boyutu kadar byte dizisi oluşturuyoruz
                byte[] bytes = new byte[fs.Length];
                fs.Read(bytes, 0, bytes.Length);
                foreach(byte b in bytes)
                {
                  Console.Write(" {0} ", b);
                }
                /* Okumak için kullanılabilecek diğer bir yöntem
                int temp = 0;
                while((temp = fs.ReadByte()) != -1)
                {
                  Console.WriteLine(temp);
                }
                */

                /* Okumak için kullanılabilecek diğer bir başka yöntem
                * while(fs.Position < fs.Length)
                  {
                    Console.Write(" {0} ", fs.ReadByte());
                  }
                * */
              }

              Console.WriteLine("\nByte bilgiler okunduktan sonraki aktif pozisyon : {0}",
                                                                            fs.Position);
              Console.ReadLine();
            }
          }
          catch(IOException ex)
          {
            Console.WriteLine(ex.Message);
            Console.ReadLine();
          }
        }
    }
}

Yukarıdaki kodu derleyip çalıştırdığımızda çıktısı aşağıdaki gibi olacaktır :

FileStream çıktısı

Görüldüğü üzere dosyaya yazmış olduğumuz (65 66 67 68) byte değerlerini okumuş olduk. Okuma işlemi yukarıda görüldüğü üzere üç farklı yöntemle yapılabilmetedir. Örnekte kullanmış olduğum yöntemde byte dizisi oluşturup sonrasında üç parametre alan FileStream.Read() metodunu çalıştırıyoruz. Oluşturduğumuz byte dizide dosyadan okuduğumuz byte verileri tutuyoruz, sonraki parametrede okumaya nereden başlanacağını belirtiyoruz ve son parametrede ne kadar uzunlukta byte veri okuyacağımızı belirtiyoruz. Sonraki foreach ifadesinde ise byte dizide yer alan her byte’ı ekrana yazıyoruz. Dikkat edileceği üzere FileStream.Length özelliğini oluşturduğumuz byte dizinin boyutunu belirlemek için kullandık. Çünkü Length özelliği dosyada tutulan byte verinin boyutunu verir.

Okumak için kullanılabilecek diğer bir yöntem ise yorumlanmış olan while döngüsü kullanmaktır. FileStream.ReadByte() metodu kullanılarak byte veriler okunabilmektedir. While döngüsünde (temp = fs.ReadByte()) != -1) kullanılan bu ifadeden anlaşılacağı üzere ReadByte() metodu veri olmadığı zaman -1 değeri döndürmekte ve okuma işlemi tamamlanmış olmaktadır.

Son yöntem isem ise while döngüsü kullanmak ancak bu sefer koşul olarak (fs.Position < fs.Length) kullanmaktır. Bu koşul stream pozisyonu dosyadaki son veriyi okuyana kadar true değerini döneceğinden tüm verileri okumuş olacaktır. Dikkat edileceği üzere FileStream ile okuma operasyonu yaparken using ifadesi kullandık çünkü bu ifade otomatik olarak işi bittiğinde Close() metodunu çağıracaktır. Eğer using ifadesi kullanmadan bu operasyonu yapıyorsanız, FileStream.Close() metodunu kullanmayı unutmayınız. Seek() metodunu kullanmak

FileStream sınıfı Seek() metodunu desteklemektedir yani stream üzerinde gerçekleştireceğiniz operasyonu istediğiniz pozisyondan başlatabiliriz. Örneğin dosya başından itibaren atlama yapılacağı zaman SeekOrigin.Begin enumeration değeri kullanılır, şimdiki pozisyondan itibaren atlama yapılacak ise SeekOrigin.Current enumeration değeri kullanılır ve dosya sonundan belirlediğiniz bir pozisyona atlama yapıcaksanız SeekOrigin.End enumeration değeri kullanılır.

Bir örnek üzerinde inceleyecek olursak :

using System;
using System.IO;

namespace StreamTest
{
    class Program
    {
        static void Main(string[] args)
        {
          try
          {
            using(FileStream fs = File.OpenRead("dosya.txt"))
            {
              // SeekOrigin.Begin kullanımı
              Console.WriteLine("Aktif Pozisyon: {0}", fs.Position);
              Console.WriteLine("Seek(1,SeekOrigin.Begin) atlama çalıştırılıyor");
              fs.Seek(1,SeekOrigin.Begin);
              Console.WriteLine("Aktif Pozisyon: {0}",fs.Position);
              Console.WriteLine("Byte verier okunuyor");
              while(fs.Position < fs.Length)
              {
                Console.Write(" {0} ", fs.ReadByte());
              }
 
              // SeekOrigin.Current kullanımı
              fs.Position = 0;
              Console.WriteLine("\n\nAktif Pozisyon: {0}",fs.Position);
              Console.WriteLine("Seek (2,SeekOrigin.Current) atlama çalıştırılıyor");
              fs.Seek(2,SeekOrigin.Current);
              Console.WriteLine("Aktif Pozisyon: {0}", fs.Position);
              Console.WriteLine("Byte verier okunuyor");
              while(fs.Position < fs.Length)
              {
                Console.Write(" {0} ", fs.ReadByte());
              }
 
              // SeekOrigin.End kullanımı
              fs.Position = 0;
              Console.WriteLine("\n\nAktif Pozisyon: {0}", fs.Position);
              Console.WriteLine("Seek(-1,SeekOrigin.End) atlama çalıştırılıyor");
              fs.Seek(-1,SeekOrigin.End);
              Console.WriteLine("Aktif Pozisyon: {0}",fs.Position);
              Console.WriteLine("Byte verier okunuyor");
              while(fs.Position < fs.Length)
              {
                Console.Write(" {0} ", fs.ReadByte());
              }
            }
            Console.ReadLine();
          }
          catch(IOException ex)
          {
            Console.WriteLine(ex.Message);
          }
        }
    }
}

Yukarıdaki kodu derleyip çalıştırdığımızda çıktısı aşağıdaki gibi olacaktır :

FileStream çıktısı

Örneğin amacı Seek() metodunu kullanarak şimdiki pozisyonu değiştirmek ve stream ile dosyanın sonuna kadar okuma operasyonu gerçekleştirmektir. Yazımızın başında FileStream kullanarak oluşturduğumuz dosyada 65 66 67 68 byte verileri bulunmaktadır. Yukarıdaki kodumuzda kullandığımız ilk Seek metodu (Seek(1, SeekOrigin.Begin)) dosya başından bir sonraki pozisyondan başlayarak dosya sonuna kadar olan verileri okuyacaktır yani 66 67 68 verilerini çıktı olarak verecektir. Unutmamak gerekir ki FileStream.Position özelliği herhangi bir atlama işleminden önce kasıtlı olarak bir operasyon yapmıyor iseniz, 0 dan yani dosya başından başlamalıdır.

İkinci Seek() metodu kullanımında (Seek (2,SeekOrigin.Current)) ise 67 68 verilerini çıktı olarak verecektir. Bu işlemden önce FileStream.Position özelliğini 0 olarak değiştirdiğimizi unutmamak gerekir. Çünkü okuma işlemi varsayılan pozisyon 0 bundan itibaren 2 veri atlanarak okuma yapacağımızı belirtiyoruz ve 3. veriden itibaren dosya sonuna kadar okuma yapacağımızı belirtmiş oluyoruz. Son Seek metodu kullanımında (Seek(-1,SeekOrigin.End)) ise dosya sonundaki son veriyi okuyacağımızı belirtiyoruz ve çıktı olarak 68 verisini alıyoruz.

Kaynak ;

Msdn FileStream Class
Msdn FileStream

Bir cevap yazın