CodeGym /Kurslar /C# SELF /FileStream sinfi

FileStream sinfi

C# SELF
Səviyyə , Dərs
Mövcuddur

1. FileStream sinfi: hissə-hissə işləmək

FileStream-i elə təsəvvür elə ki, sənin diskindəki fayla birbaşa qoşulan su borusudur. Bu borudan sən məlumatları idarəli şəkildə keçirə bilərsən: fayla baytlar göndərmək (yazmaq) və ya fayldan baytlar almaq (oxumaq). Daha yüksək səviyyəli metodlardan fərqli olaraq, hansılar ki, sadəcə "sənə bir stəkan su verir", FileStream sənə birbaşa "krana" çıxış verir və lazım olduqca suyun (baytların) axınını tənzimləməyə imkan verir.

FileStream baytlar səviyyəsində işləyir. Yəni, o, heç maraqlanmır ki, bu mətn faylıdır, yoxsa şəkil; onun üçün hər şey sadəcə bayt ardıcıllığıdır. Əgər sən mətn faylları ilə FileStream vasitəsilə işləyirsənsə, sətirləri baytlara çevirməyi (məsələn, kodlaşdırma istifadə edərək, UTF-8) və oxuyanda əksinə çevirməyi özün etməlisən.

Bəs FileStream nə vaxt lazımdır?

  • Çox böyük fayllarla işləmək: Fayl o qədər böyükdür ki, onu tam olaraq RAM-a yükləmək mümkün deyil və ya məntiqli deyil (məsələn, gigabaytlıq loglar, video fayllar). FileStream məlumatları hissə-hissə (chunk-larla) oxumağa və ya yazmağa imkan verir, bu da yaddaşı daha səmərəli istifadə edir.
  • Binary məlumatlar: Əgər sən adi mətn olmayan fayllarla işləyirsənsə (şəkillər, audio, video, serializasiya olunmuş obyektlər, verilənlər bazası faylları), FileStream əsas alətdir, çünki o, birbaşa baytlara çıxış verir.
  • Giriş rejimlərinə detallı nəzarət: Faylı həm oxumaq, həm də yazmaq üçün açmaq lazımdır? Yaxud elə açmaq istəyirsən ki, başqa proqramlar ona çıxış edə bilməsin? Yaxud əksinə, bir neçə proses eyni anda faylı oxuya bilsin? FileStream bütün bu rejimlərə tam nəzarət verir.
  • Asinxron əməliyyatlar: Müasir yüksək performanslı tətbiqlərdə (məsələn, web-serverlərdə) fayl əməliyyatlarının əsas axını bloklamaması çox vacibdir. FileStream asinxron metodları (ReadAsync, WriteAsync) dəstəkləyir, proqramın cavabdeh qalmasına imkan verir.
  • Hissəvi oxuma/yazma və ya istənilən yerə çıxış: Əgər sən fayldan konkret bir yerdən (məsələn, 500-cü baytdan) məlumat oxumaq və ya faylın ortasına məlumat yazmaq istəyirsənsə, FileStream fayl göstəricisinin mövqeyini idarə etməyə imkan verir.

Tipik yeni başlayan səhvi: Ən sadə tapşırıqlar üçün, məsələn, kiçik fayla bir sətir mətn yazmaq üçün FileStream istifadə etmək. Belə hallarda bu artıqdır. FileStream spesifik və daha çətin ssenarilər üçün alətdir.

2. FileStream yaratmaq və açmaq

Faylla FileStream vasitəsilə işləməyə başlamaq üçün onun instansiyasını yaratmalısan. Bu, bir neçə vacib parametr qəbul edən konstruktorla edilir və bu parametrlər sənə faylla necə işləmək istədiyini təyin etməyə imkan verir.

using System;
using System.IO;   // FileStream ilə işləmək üçün mütləqdir
using System.Text; // Kodlaşdırmalarla işləmək üçün (sətirləri baytlara və əksinə çevirmək)

FileStream ilə mətn faylının əsas oxunması nümunəsi:

Əvvəlcə oxumaq üçün bir fayl yaradaq.

// Nümunə üçün sadə mətn faylı yaradaq
string path = "example_filestream.txt";

// Oxumaq üçün stream açırıq.
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

// Oxunan məlumatları müvəqqəti saxlamaq üçün buffer (bayt massivi) yaradırıq.
byte[] buffer = new byte[fs.Length];

// Stream-dən buffer-ə baytları oxuyuruq.
int bytesRead = fs.Read(buffer, 0, buffer.Length);

// Oxunan baytları lazım olan kodlaşdırma ilə (məsələn, UTF-8) yenidən sətirə çeviririk.
string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine("FileStream ilə fayldan oxundu: " + content);

fs.Close(); //stream-i bağlayırıq

Əsas parametrlər

  • FileMode: faylı necə açmaq lazımdır (Open, Create, Append OpenOrCreate və s.)
  • FileAccess: faylla nə etmək lazımdır (Read, Write, ReadWrite)
  • FileShare: başqa proseslər eyni anda bu fayldan istifadə edə bilərmi (adətən sadə tapşırıqlarda lazım olmur)

Tipik səhv: Əgər sən FileStream-i bağlamağı unutsan, fayl "kilidlənə" bilər — sonra onu silmək və ya yenidən açmaq mümkün olmayacaq!

3. FileStream konstruktorunun parametrləri

FileStream konstruktoru sənə faylı necə açmaq istədiyini çox dəqiq tənzimləməyə imkan verir. Əsas parametrləri bunlardır:

new FileStream(
    string path,           // Faylın yolu (absolute və ya relative)
    FileMode mode,         // Faylı necə açmaq (yaratmaq, açmaq, yenidən yazmaq və s.)
    FileAccess access,     // Hansı giriş icazəsi var (yalnız oxumaq, yalnız yazmaq, oxumaq+yazmaq)
    FileShare share        // Fayl açıq olanda başqa proseslər ona necə müraciət edə bilər (optional)
);

Gəlin FileModeFileAccess enum-larının dəyərlərinə baxaq:

FileMode (Fayl açma rejimi):

Fayl açılarkən əməliyyat sisteminin onunla necə davranacağını təyin edir.

  • FileMode.Open: Mövcud faylı açır. Əgər göstərilən yolda fayl tapılmasa, FileNotFoundException atılır.
  • FileMode.Create: Yeni fayl yaradır. Əgər belə adla fayl artıq varsa, o tamamilə yenidən yazılır (məzmunu silinir).

FileAccess (Fayla giriş hüquqları):

Proqramının açıq faylla hansı əməliyyatları edə biləcəyini təyin edir.

  • FileAccess.Read: Fayl yalnız oxumaq üçün açılıb. Ona məlumat yazmaq mümkün deyil.
  • FileAccess.Write: Fayl yalnız yazmaq üçün açılıb. Ondan məlumat oxumaq mümkün deyil.
  • FileAccess.ReadWrite: Fayl həm oxumaq, həm də yazmaq üçün açılıb. Bu ən çevik, amma potensial olaraq daha çətin rejimdir, çünki stream-də mövqeyə nəzarət etməlisən.

FileShare (Paylaşılan giriş):

Başqa proseslərin sənin proqramın faylı açıq saxlayarkən həmin faylı necə aça biləcəyini təyin edir. Bloklanmaların qarşısını almaq üçün vacibdir.

  • FileShare.Read: Başqa proseslər faylı oxuya bilər, amma yaza bilməz, sən FileStream ilə açanda.
  • FileShare.Write: Başqa proseslər fayla yaza bilər, amma oxuya bilməz, sən FileStream ilə açanda.
  • FileShare.ReadWrite: Başqa proseslər faylı həm oxuya, həm də yaza bilər. Bu ən liberal rejimdir, amma bir neçə proqram eyni anda faylı dəyişəndə konfliktlər ola bilər.

Rejimlər barədə daha ətraflı növbəti mühazirədə.

4. FileStream ilə məlumat oxumaq və yazmaq

FileStream ilə işləyəndə sən baytlarla işləyirsən. Yəni, mətn məlumatları üçün sətirlərlə bayt massivləri arasında çevirmə aparmalısan, bunun üçün kodlaşdırma siniflərindən (məsələn, System.Text.Encoding.UTF8) istifadə olunur.

FileStream ilə fayla məlumat yazmaq

Yazanda sən məlumatını (məsələn, sətir) bayt massivinə çevirirsən və sonra bu baytları stream-ə yazırsan.

string outputPath = "user_data.txt";
string userName = "İvan Petrov";

// Sətiri UTF-8 kodlaşdırması ilə bayt massivinə çeviririk
byte[] userNameBytes = Encoding.UTF8.GetBytes(userName);

// Yazmaq üçün FileStream açırıq.
FileStream fsWrite = new FileStream(outputPath, FileMode.Create, FileAccess.Write);

// Bayt massivini stream-ə yazırıq.
fsWrite.Write(userNameBytes, 0, userNameBytes.Length);
Console.WriteLine($"Ad '{userName}' uğurla '{outputPath}' faylına yazıldı.");

fsWrite.Close(); //stream-i bağlayırıq

FileStream ilə fayldan məlumat oxumaq

Oxuyanda sən stream-dən baytları buffer-ə yığırsan və sonra bu baytları lazım olan formata (məsələn, sətirə) çevirirsən.

string inputPath = "user_data.txt";

// Oxumaq üçün FileStream açırıq
FileStream fsRead = new FileStream(inputPath, FileMode.Open, FileAccess.Read);

// Fayl ölçüsündə buffer yaradırıq ki, bütün baytları oxuyaq.
byte[] buffer = new byte[fsRead.Length];

// Stream-dən buffer-ə baytları oxuyuruq. 
int bytesRead = fsRead.Read(buffer, 0, buffer.Length);

// Oxunan baytları sətirə çeviririk.
string loadedUserName = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Fayldan ad (FileStream ilə): {loadedUserName}");

fsRead.Close(); //stream-i bağlayırıq

5. Böyük fayllarla işləmək

FileStream-in File.ReadAll... metodlarından əsas üstünlüyü odur ki, sən faylı hissə-hissə oxuya və yaza bilirsən, bu da RAM-a tam yerləşməyən böyük fayllarla işləmək üçün kritikdir.

Böyük faylları hissə-hissə oxumaq

Faylı hissə-hissə oxuyanda sən sabit ölçülü buffer (bayt massivi) istifadə edirsən və faylın sonuna çatana qədər ora məlumat yığırsan.

string bigFilePath = "bigfile.bin";     //böyük fayl 
int bufferSize = 4096;                  // Buffer ölçüsü, məsələn, 4 KB
byte[] buffer = new byte[bufferSize];   // Buffer yaradırıq
int bytesRead;                          // Faktiki oxunan bayt sayı
long totalBytesRead = 0;                // Ümumi oxunan bayt sayı

// stream açırıq 
FileStream fs = new FileStream(bigFilePath, FileMode.Open, FileAccess.Read);
int chunkNumber = 1;

// fs.Read() müsbət bayt sayı qaytardıqca dövr davam edir
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
    totalBytesRead += bytesRead;
    Console.WriteLine($"Hissə {chunkNumber++}: {bytesRead} bayt oxundu. Cəmi: {totalBytesRead} bayt.");

    // !!! Burada oxunan "hissə" məlumatların emalı gedir !!!
   
}

fs.Close(); //stream-i bağlayırıq

Vacib qeyd: fs.Read(buffer, offset, count) metodu buffer-i tam dolduracağına zəmanət vermir. O, faktiki oxunan baytların sayını qaytarır və bu, count-dan az ola bilər (xüsusilə faylın sonunda). Məlumatı düzgün emal etmək üçün həmişə bytesRead dəyərindən istifadə et.

Böyük məlumatları hissə-hissə yazmaq

Oxumaq kimi, böyük həcmdə məlumatı fayla hissə-hissə yaza bilərsən.

string bigOutputPath = "big_output.txt";
string longText = new string('A', 1_000_000); // Bir milyon 'A' simvolundan ibarət sətir
byte[] longTextBytes = Encoding.UTF8.GetBytes(longText);
int writeBufferSize = 1024; // Hər dəfə 1 KB yazırıq

// FileMode.Create: fayl yaradırıq
FileStream fsWrite = new FileStream(bigOutputPath, FileMode.Create, FileAccess.Write);

for (int i = 0; i < longTextBytes.Length; i += writeBufferSize)
{
    // Cari iterasiyada neçə bayt yazmaq lazım olduğunu hesablayırıq
    int bytesToWrite = Math.Min(writeBufferSize, longTextBytes.Length - i);

    // longTextBytes-dan 'i' offset-dən başlayaraq məlumat hissəsini yazırıq
    fsWrite.Write(longTextBytes, i, bytesToWrite);
    Console.WriteLine($"Yazıldı {bytesToWrite} bayt");
}

fsWrite.Close(); //stream-i bağlayırıq

6. Gözlənilməz tələlər və tipik səhvlər

FileStream kimi güclü alətlə işləyəndə belə, yadda saxlamalı olduğun bəzi nüanslar var:

Bufferləşmə və Flush(): Dediyimiz kimi, məlumat RAM-da buffer-də qala bilər və diska çatmaya bilər. Əgər using istifadə etmirsənsə və Close()/Dispose() çağırmırsansa, sadəcə proqramı bitirsən, məlumat itə bilər. Məlumatın yazılmasına zəmanət üçün həmişə Flush() çağır və ya using/Close()-a güvən.

İstisnaların emalı: İ/O səhvləri (məsələn, IOException - disk dolu olanda yazmağa cəhd, UnauthorizedAccessException - icazə çatışmazlığı, FileNotFoundException - Open rejimində mövcud olmayan faylı açmağa cəhd) adi haldır. FileStream ilə əməliyyatları həmişə try-catch blokuna qoy.

Kodlaşdırmalar: FileStream ilə mətn məlumatı işləyəndə (o, baytlarla işləyir), düzgün kodlaşdırmanı seçmək tam sənin məsuliyyətindir (Encoding.UTF8, Encoding.ASCII, Encoding.Unicode və s.) sətirləri bayta və əksinə çevirəndə. Səhv kodlaşdırma "krakozyabra"ya səbəb olacaq.

Mövqeyə nəzarət: Seek() istifadə edirsənsə, offsetorigin parametrlərinə diqqət et ki, faylın hüdudlarından kənara və ya gözlənilməz yerə getməyəsən.

Fayl kilidləri (FileShare): Əgər sən faylı FileShare.None ilə açırsansa, başqa proseslər ona çıxış edə bilməyəcək. Bu problem ola bilər, əgər başqa proqram (və ya hətta sənin proqramında başqa thread) eyni fayldan istifadə etməyə çalışsa. Həmişə ən uyğun FileShare rejimini seç.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION