1. Giriş
Proqramlaşdırmada fayllarla təhlükəsiz işləmə dedikdə təkcə viruslardan və hakerlərdən qorunma yox, həm də düzgün xətaların, låklamanın, icazələrin, səhv yolların və digər gözlənilməz problemlərin idarə olunması nəzərdə tutulur. Laqeyd yanaşma məlumat itkisinə, proqramın donmasına, qəribə buglara və ya məşhur IOException istisnasına gətirib çıxara bilər.
Budur klassik situasiyalar, hansıları üzərində işləməyi öyrənməliyik:
- Fayl mövcud deyil, amma biz onu oxumağa çalışırıq (və ya əksinə: fayl artıq yaradılıb, amma biz "yalnız yoxdursa yarat" rejimində yazmaq istəyirik).
- Fayl başqa proqram tərəfindən açılıb və bloklanıb.
- İstifadəçinin faylı və ya qovluğu oxuma/yazma icazəsi yoxdur.
- Fayla aparan yol səhvlidir və ya qadağan olunmuş simvolları ehtiva edir.
- Əməliyyat gözlənilmədən dayandırılıb (məsələn, disk dolub).
- Pis praktika: açıq faylları qoymaq, axınları bağlamamaq.
Xoşbəxtlikdən, .NET bu problemləri həll etmək üçün bütün vasitələri verir. Onlar sadədir — amma təhlükəsizlik kəmərlərini taxmaq kimi, əsas məsələ onlardan istifadə etməkdir.
2. Fayllarla təhlükəsiz işin əsas prinsipləri
Faylın mövcudluğunu və icazələri əvvəlcədən yoxlayın
Faylı oxumazdan əvvəl onun mövcud olduğunu yoxlayın (məsələn, File.Exists vasitəsilə), xüsusən yol istifadəçidən gəlirsə:
string path = "test.txt";
if (!File.Exists(path))
{
Console.WriteLine("Səhv: fayl tapılmadı.");
return;
}
Yazmadan əvvəl qovluğun mövcudluğunu və yazmaq icazənizin olduğunu təsdiqləyin (və ya xətaları üst səviyyəyə atın).
Heç vaxt axınları açıq qoymayın
Axınların avtomatik bağlanması üçün using açar sözündən istifadə edin — bu ən yaxşı praktikadır:
using var writer = new StreamWriter("output.txt");
writer.WriteLine("Hello, files!");
// Burada fayl artıq bağlıdır, hətta səhv baş verərsə də!
Bu "donmuş" låklardan və resurs sızmalarından qoruyur.
Həmişə istisnaları tutun
Hər hansı fayl əməliyyatı istisna ata bilər. Fayl yeni olsa belə — onu çağıranda silinə və ya köçürülə bilər. try-catch konstruksiyasından istifadə edin:
try
{
using var reader = new StreamReader("data.txt");
string line = reader.ReadLine();
Console.WriteLine(line);
}
catch (FileNotFoundException)
{
Console.WriteLine("Fayl tapılmadı.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Fayla giriş icazəsi yoxdur.");
}
catch (IOException ex)
{
Console.WriteLine($"I/O xətası: {ex.Message}");
}
İstifadəçi inputuna inanmayın
Əgər fayl yolunu istifadəçi daxil edirsə, onu səhv yazma və ya proqramı qırmağa cəhd edə bilər. Path.GetInvalidPathChars() nəzərdən keçirmək tövsiyə olunur:
try
{
string userPath = Console.ReadLine()!;
if (string.IsNullOrWhiteSpace(userPath))
{
Console.WriteLine("Yol boş ola bilməz!");
return;
}
// Əlavə: qadağan olunmuş simvolları yoxla
foreach (char c in Path.GetInvalidPathChars())
if (userPath.Contains(c))
{
Console.WriteLine("Yol qəbulagörənsiz simvollar ehtiva edir.");
return;
}
// Bundan sonra faylla təhlükəsiz işləmək olar
}
catch (Exception ex)
{
Console.WriteLine("Yolun validasiyasinda səhv: " + ex.Message);
}
3. Praktik nümunə: faylı "ciddi" oxuyuruq
Gəlin tədris layihəmizi bir az təkmilləşdirək. Tutaq ki, istifadəçinin daxil etdiyi adla faylı oxuyub məzmununu ekrana çıxarmalıyıq. Bütün bunlar — xətaların emalı və kodlaşdırma nəzərə alınmaqla.
Console.Write("Faylın yolunu daxil edin: ");
string? path = Console.ReadLine();
// Yolun validasiyası
if (string.IsNullOrWhiteSpace(path))
{
Console.WriteLine("Yol boş ola bilməz!");
return;
}
foreach (char c in Path.GetInvalidPathChars())
if (path.Contains(c))
{
Console.WriteLine("Yol qəbulagörənsiz simvollar ehtiva edir.");
return;
}
// Faylı oxumağa çalışaq
try
{
if (!File.Exists(path))
{
Console.WriteLine("Fayl tapılmadı.");
return;
}
// Məsələn, UTF-8 kodlaşdırmasını açıq göstərək
using var reader = new StreamReader(path, Encoding.UTF8);
string content = reader.ReadToEnd();
Console.WriteLine("Faylın məzmunu:");
Console.WriteLine(content);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Faylı oxumaq üçün icazə yoxdur.");
}
catch (IOException ex)
{
Console.WriteLine("I/O xətası: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Gözlənilməz xəta: " + ex.Message);
}
Qeyd edin ki, əgər fayl adını daxil etdikdən sonra fayl yoxa çıxsa, bu kod çökməyəcək: istisna tutularaq emal ediləcək. Axın using blokundan çıxdıqda avtomatik bağlanacaq — səhv olsa belə.
4. Yazma zamanı: məlumat itkisinə yol verməyin
Faylı yazarkən, xüsusilə overwrite rejimində (false ikinci parametr kimi StreamWriter konstruktorunda), təsadüfən vacib məlumatı silmək riski var. Bir neçə tövsiyə:
Mövcud faylı üst-üstə yazıb-yazmayacağınızı yoxlayın
Bəzən fayl artıq varsa istifadəçidən soruşmaq faydalıdır:
if (File.Exists(path))
{
Console.WriteLine("Diqqət: fayl artıq mövcuddur. Üst-üstə yazılsın? (y/n)");
string answer = Console.ReadLine()!;
if (!answer.Equals("y", StringComparison.OrdinalIgnoreCase))
return;
}
Yazma zamanı append rejimindən istifadə edin
using var writer = new StreamWriter("log.txt", append: true);
writer.WriteLine(DateTime.Now + ": yeni jurnal qeydi.");
Keçmiş məlumatlar itməyəcək.
5. Race condition və konfliktlərdən qorunma
Bəzən bir faylı eyni anda bir neçə proqram aça bilər (məsələn, sizin C# tətbiqi və Notepad++). Bu səhvlərə gətirə bilər. Default olaraq StreamReader və StreamWriter FileShare-ə əsaslanan paylaşım rejimlərindən istifadə edir — yəni ya başqalarına oxumağa icazə verirlər, ya da yox.
Bunu açıq şəkildə idarə etmək olar:
using var stream = new FileStream("data.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
using var reader = new StreamReader(stream, Encoding.UTF8);
// Oxuma...
- FileShare.Read: başqaları yalnız oxuya bilər.
- FileShare.None: paylaşım yoxdur — fayl tamamilə "səndir".
Əgər faylı həm oxumaq, həm yazmaq üçün müxtəlif proqramlar lazım olarsa — müvafiq rejimi seçin, amma bunun nəticələrini yaxşı başa düşün.
6. Gözlənilməz istisnalar və onları necə idarə etmək
Bütün qaydalara əməl etsəniz belə, məsələn, diskdə yerin olmaması, sıradan çıxmış flash və ya virus kimi uğursuzluqlar baş verə bilər. Bir neçə "egzotik" istisna və onları necə tutmaq olar:
- PathTooLongException — fayl yolu çox uzundur (köhnə Windows versiyalarında 260 simvoldan artıq).
- DirectoryNotFoundException — göstərilmiş qovluq tapılmadı.
- DriveNotFoundException — məsələn, yol "Z:\\file.txt" kimidirsə və Z diski yoxdur.
- NotSupportedException — məsələn, yol icazəsiz kombinasiyanı ehtiva edir.
Bu tip istisnalar üçün ayrıca blok etmək və ən azından loglamaq məsləhətdir.
7. Atomik yazma üçün müvəqqəti fayllardan istifadə
Klassik problem: faylı yazırsan, amma proqram ortasında bağlanır — nəticədə fayl korlanır. Peşəkar proqramlar adətən "atomik" yazma strategiyasından istifadə edirlər:
- Məzmunu müvəqqəti fayla yazmaq (məsələn, "file.txt.tmp").
- Müvəqqəti faylı əsas faylın üstünə köçürmək (bu əməliyyat fayl sistemi səviyyəsində adətən atomikdir) — File.Replace.
- Köhnə fayl tamamilə əvəz olunur və ya heç dəyişmir — yarımçıq məlumat olmaz.
Nümunə:
string tempPath = path + ".tmp";
try
{
using var writer = new StreamWriter(tempPath, false, Encoding.UTF8);
// Hər şeyi müvəqqəti fayla yazırıq
writer.Write(contentForSave);
// Uğurlu yazıdan sonra əsas faylı əvəz edirik
File.Replace(tempPath, path, null); // Müvəqqəti faylı əsas faylla atomik şəkildə əvəz edir
}
catch (Exception ex)
{
Console.WriteLine("Faylın saxlanmasında xəta: " + ex.Message);
// tempPath faylını silmək yaxşıdır, əgər lazım deyilsə
}
Reallıqda bir çox redaktorlar və ofis paketləri məlumatların konsistentliyini təmin etmək üçün bu yanaşmanı istifadə edir.
8. Təhlükəsiz giriş üçün wrapper-klaslardan istifadə
.NET "təhülkəsiz" fayl əməliyyatları üçün köməkçi metodlar təklif edir. Məsələn, File.ReadAllText və File.WriteAllText avtomatik olaraq faylı açıb oxuyur/yazır və bağlayır. Amma onları da try-catch ilə örtmək lazımdır:
try
{
string text = File.ReadAllText("settings.json", Encoding.UTF8);
// Məlumatla işləyirik...
}
catch (Exception ex)
{
Console.WriteLine("Faylla işləmədə xəta: " + ex.Message);
}
Böyük fayllar üçün isə stream-lərdən istifadə edib faylı hissə-hissə oxuyun, birdən-birə bütün RAM-ı doldurmayasınız.
GO TO FULL VERSION