1. Giriş
Gəlin təsəvvür edək ki, bir çaydan su var. Krana açırsan — su axmağa başlayır. Çoxlu su yığıb birdən tökmək olar, ya da çaydanı az-az doldurmaq olar. Fayllarla da eyni vəziyyətdir — heç də həmişə bütün faylı bir anda yaddaşa yükləmək rahat və ya mümkün olmur. Fayllar böyük ola bilər, bəzən isə məlumat mənbəyi fayl yox, məsələn, şəbəkə bağlantısı olur və məlumatlar tədricən gəlir.
Əgər biz həmişə sadəcə byte massivi ilə işləməyə çalışsaydıq, böyük fayllarda yaddaşımız tez bitərdi, həm də "sonsuz" məlumat axınları üçün (məsələn, video və ya audio axınları) bu yanaşma işləməzdi. Bax, burada axın konsepsiyası köməyə gəlir!
.NET-də axın — məlumatlara ardıcıl çıxış üçün abstraksiyadır: mənbənin nə olduğu fərq etmir — fayl, şəbəkə, yaddaş, ya da hətta sıxılmış arxiv kimi ekzotik bir şey. Axın sənə məlumatları hissə-hissə oxumağa və yazmağa imkan verir, adətən bloklarla və ya baytlarla.
Əsas ideya:
- Axın — məlumat ötürmək üçün kanaldır. O, konveyer kimidir: sən "qoya" (yaza) və ya "götürə" (oxuya) bilərsən, harada və necə saxlanıldığını birbaşa düşünmədən.
- Məlumatlar ardıcıl gəlir: növbəti hissəni yalnız əvvəlkini oxuduqdan sonra oxuya bilərsən (ya da əksinə, əgər geri çəkmə dəstəklənirsə).
- Çox vaxt bütün məlumatı bir anda yaddaşda saxlamırsan (və kompüter buna görə sənə təşəkkür edəcək).
Bu abstraksiya .NET-də demək olar ki, bütün giriş-çıxış əməliyyatlarının əsasındadır: fayllarla, şəbəkələrlə, arxivlərlə, hətta konsolla işləmək!
2. Axınlar System.IO.Stream
İrsi və arxitektura: System.IO.Stream
.NET-də demək olar ki, bütün axınlar abstrakt System.IO.Stream sinifindən miras alır. O, axını oxumaq, yazmaq, axında hərəkət etmək və onu idarə etmək üçün əsas metodları müəyyən edir.
classDiagram
class Stream {
+Read()
+Write()
+Seek()
+CanRead
+CanWrite
+CanSeek
+Length
+Position
}
class FileStream
class MemoryStream
class NetworkStream
class CryptoStream
Stream <|-- FileStream
Stream <|-- MemoryStream
Stream <|-- NetworkStream
Stream <|-- CryptoStream
- Stream — əsas abstrakt sinif
- FileStream — fayllarla işləmək üçün
- MemoryStream — yaddaşdakı məlumatlarla işləmək üçün
- NetworkStream — şəbəkə ilə işləmək üçün
- CryptoStream — şifrələmə/deşifrələmə üçün
Axının əsas property və metodları ilə qısa tanışlıq
| Property / Metod | Təsviri |
|---|---|
|
Bu axından oxumaq mümkündürmü |
|
Bu axına yazmaq mümkündürmü |
|
Axında hərəkət etmək mümkündürmü (hamısı dəstəkləmir) |
|
Axının uzunluğu (əgər dəstəklənirsə — bütün axınlarda olmur) |
|
Axında cari mövqe |
|
Məlumat oxumaq |
|
Məlumat yazmaq |
|
Axında hərəkət etmək |
|
Buffer-i sıfırlamaq (bütün yığılanı axına yazmaq) |
/ |
Axını bağlamaq və resursları azad etmək |
Gəlin baxaq, bu "praktikada" necə görünür.
3. Nümunə: Faylları Stream ilə oxumaq və yazmaq
Axını "işdə" görmək üçün kifayət qədər minimal bir nümunə:
// Yazmaq üçün faylı açırıq
using var stream = new FileStream("numbers.bin", FileMode.Create);
// Təsəvvür et ki, 1-dən 10-a qədər ədədləri fayla yazmaq istəyirik
for (int i = 1; i <= 10; i++)
{
byte val = (byte)i;
stream.WriteByte(val); // Hər dəfə bir bayt yazırıq
}
// Faylı açıq şəkildə bağlayırıq ki, onu oxumaq üçün açaq
stream.Close();
// İndi bu ədədləri geri oxumağa çalışaq
using var stream2 = new FileStream("numbers.bin", FileMode.Open);
int value;
while ((value = stream2.ReadByte()) != -1)
{
Console.WriteLine(value); // 1, 2, ... 10 çıxacaq
}
Burada biz FileStream istifadə edirik, hansı ki, axının tam mənasında axındır: sən məlumatı bloklarla və ya baytlarla oxuyur və yazırsan.
Axın növləri: harada rast gələ bilərsən?
Axın — mütləq diskdəki fayl deyil. Budur, axın konsepsiyasının istifadə olunduğu bir neçə nümunə:
- Diskdə fayl (məsələn, FileStream — ən çox rast gəlinən hal)
- Operativ yaddaşda axın (MemoryStream — müvəqqəti və ya aralıq məlumatlar üçün rahatdır)
- Şəbəkə bağlantısı (NetworkStream)
- Sıxılma/arxivləşdirmə (GZipStream, DeflateStream)
- Şifrələmə (CryptoStream)
- Konsol giriş/çıxış (bəli-bəli!) — texniki olaraq, bunlar da axındır
Bu, kodunu yazmağa imkan verir ki, konkret mənbə/qəbuledici barədə düşünməyəsən: əgər kodun axınla işləyirsə, deməli o, universaldır!
4. Faydalı nüanslar
Oxumaq və yazmaq — məlumatı hissə-hissə ötürmək əməliyyatlarıdır. Adətən byte massivi və Read, Write metodları ilə.
Nümunə: faylı bloklarla oxumaq
byte[] buffer = new byte[1024]; // 1024 baytlıq buffer (1 KB)
using var stream = new FileStream("bigfile.bin", FileMode.Open);
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
// buffer daxilində yalnız bytesRead baytı emal edirik
int sum = 0;
for (int i = 0; i < bytesRead; i++)
sum += buffer[i];
Console.WriteLine($"Blokun cəmi: {sum}");
}
Bu yanaşma hər yerdə istifadə olunur — antiviruslardan tutmuş musiqi pleyerlərinə qədər.
Axında mövqe dəyişmək (Position, Seek)
Axınların çoxunda (məsələn, fayl axınlarında) məlumatlar üzrə hərəkət etmək olar — sadəcə "növbəti hissə"ni oxumaq yox, konkret mövqeyə keçib oradan işləmək.
using var stream = new FileStream("numbers.bin", FileMode.Open);
stream.Position = 5; // 6-cı bayta keçir (sıfırdan sayılır)
int value = stream.ReadByte();
Console.WriteLine($"Fayldakı 6-cı bayt: {value}");
Axınlar yalnız oxumaq, yalnız yazmaq və ya hər ikisi üçün ola bilər
Bəzi axınlar yalnız bir variantı dəstəkləyir:
- Yazmaq üçün açılmış fayl: yalnız Write()
- Şəbəkə məlumatı oxumaq üçün axın: yalnız Read()
- Bəzi ekzotik hallarda (məsələn, printerə çıxış axını) ümumiyyətlə "geri çəkmək" və ya axında mövqe dəyişmək mümkün deyil (geri getmək olmur).
Dəstəklənən əməliyyatları CanRead, CanWrite, CanSeek property-ləri ilə yoxla:
using var stream = new FileStream("myfile.txt", FileMode.OpenOrCreate);
if (stream.CanRead)
Console.WriteLine("Oxumaq dəstəklənir");
if (stream.CanWrite)
Console.WriteLine("Yazmaq dəstəklənir");
if (stream.CanSeek)
Console.WriteLine("Faylda hərəkət etmək olar");
Axınlarda bufferləşmə
Demək olar ki, bütün axınlar performansı artırmaq üçün daxili bufferlərdən istifadə edir. Bufferləşmə diskə/şəbəkəyə müraciətləri azaldır: məlumatlar daxildə toplanır, sonra toplu şəkildə verilir/yazılır.
Flush() metodu buffer-i sıfırlamağa imkan verir (məsələn, hər şeyin diskə yazıldığına əmin olmaq üçün):
using var stream = new FileStream("log.txt", FileMode.Append);
byte[] bytes = Encoding.UTF8.GetBytes("Salam, Stream!\n");
stream.Write(bytes, 0, bytes.Length);
stream.Flush(); // Yazının mütləq diskə getdiyinə zəmanət verir
Əgər həyati vacib məlumat yazırsansa (məsələn, ödəniş əməliyyatları!), Flush() çağırmaq — sənin dostundur.
5. Axınlarla işləyərkən tipik səhvlər
Çox vaxt yeni başlayanlar belə səhvlər edirlər:
Axını bağlamağı unudurlar (və yaddaş sızmaları, "asılı" fayllar və başqa əyləncələr alırlar).
Mətn və binar axınları qarışdırırlar — sətiri bayt metodu ilə yazmağa çalışırlar, sonra "qaraqara"lar alırlar.
Çox kiçik buffer istifadə edirlər (ya da ümumiyyətlə buffersiz) — əməliyyatlar ləng olur.
Düşünürlər ki, Read() həmişə istədiyin qədər bayt oxuyur — əslində, daha az qaytara bilər; həmişə qaytarılan dəyəri yoxlamaq lazımdır.
Bütün axınların mövqe dəyişməni (Seek) dəstəkləmədiyini nəzərə almırlar, xüsusilə şəbəkə axınlarında.
Məsələn:
// Pis nümunə: fayldan bütün baytları oxuyarkən həqiqətən neçə bayt oxunduğunu yoxlamırıq
byte[] buffer = new byte[1024];
using (var stream = new FileStream("data.bin", FileMode.Open))
{
int bytesRead = stream.Read(buffer, 0, 1024);
// bytesRead fayl kiçikdirsə, 1024-dən az ola bilər!
}
GO TO FULL VERSION