1. Qovluqdakı faylların siyahılanması
Faylların siyahısını almaq: Files.list və Files.walk
Java-da qovluğun məzmunu ilə işləmək üçün iki çox faydalı metod var:
- Files.list(Path) — verilən qovluqda yuxarı səviyyədəki bütün fayl və qovluqların axınını (Stream<Path>) qaytarır.
- Files.walk(Path) — qovluqdakı və onun bütün alt qovluqlarındakı bütün fayl və qovluqların axınını qaytarır (rekursiv).
Nümunə: qovluqdakı bütün fayl və qovluqların çıxarılması data
import java.nio.file.*;
import java.io.IOException;
public class ListFilesExample {
public static void main(String[] args) throws IOException {
Path dir = Paths.get("data");
// Yuxarı səviyyədəki bütün fayl və qovluqların axınını alırıq
try (var stream = Files.list(dir)) {
stream.forEach(System.out::println);
}
}
}
Əgər bütün iç-içə qovluqlardan (rekursiv) keçmək istəyirsinizsə, Files.walk istifadə edin:
try (var stream = Files.walk(dir)) {
stream.forEach(System.out::println);
}
Diqqət:
Files.walk struktur böyükdürsə, çox sayda fayl qaytara bilər — “bütün kainatın siyahısını” görmək istəmirsinizsə, bunu disk kökündə işə salmayın!
Vizualizasiya: list və walk arasındakı fərq
| Metod | Nə qaytarır |
|---|---|
|
Yalnız cari qovluğun məzmunu |
|
Bütün fayl və qovluqlar, o cümlədən alt qovluqlar |
2. Faylların filtrlənməsi
Axınlarla (Stream<Path>) işləməyin əsas üstünlüklərindən biri — faylları istənilən əlamətə görə filtrləmək imkanıdır: uzantı, ad, ölçü, yaradılma tarixi və s.
Uzantıya görə filtrləmə
Tutaq ki, bizə yalnız .txt-faylları lazımdır. Onları belə filtrləmək olar:
try (var stream = Files.list(dir)) {
stream
.filter(path -> path.toString().endsWith(".txt"))
.forEach(System.out::println);
}
Ada görə filtrləmə
Adı "report" ilə başlayan bütün fayllar:
stream
.filter(path -> path.getFileName().toString().startsWith("report"))
.forEach(System.out::println);
Ölçüyə görə filtrləmə
Yalnız “böyük” faylları götürək (1 MB-dən böyük):
stream
.filter(path -> {
try {
return Files.size(path) > 1_000_000;
} catch (IOException e) {
return false;
}
})
.forEach(System.out::println);
Tarixə görə filtrləmə
Məsələn, son 7 gün ərzində dəyişdirilmiş fayllar:
import java.time.*;
import java.nio.file.attribute.*;
stream
.filter(path -> {
try {
FileTime lastModified = Files.getLastModifiedTime(path);
return lastModified.toInstant().isAfter(Instant.now().minus(Duration.ofDays(7)));
} catch (IOException e) {
return false;
}
})
.forEach(System.out::println);
Hamısı birlikdə: kombinə edilmiş filtrləmə
Filtrləri “restorandakı kimi” kombinə etmək olar: məsələn, bir aydan köhnə və 10 KB-dən böyük bütün .log-fayllar:
stream
.filter(path -> path.toString().endsWith(".log"))
.filter(path -> {
try {
return Files.size(path) > 10_000;
} catch (IOException e) {
return false;
}
})
.filter(path -> {
try {
FileTime lastModified = Files.getLastModifiedTime(path);
return lastModified.toInstant().isBefore(Instant.now().minus(Duration.ofDays(30)));
} catch (IOException e) {
return false;
}
})
.forEach(System.out::println);
3. Faylların kütləvi nüsxələnməsi və silinməsi
Kütləvi əməliyyatlar — sadədir: faylların axınını əldə edirik və sonra hər elementə lazım olan funksiyanı tətbiq edirik. Əsas odur ki, mümkün xətaları unutmayasınız (məsələn, fayl məşğuldur və ya artıq mövcuddur).
Bütün faylları bir qovluqdan digərinə nüsxələmək
import java.nio.file.StandardCopyOption;
Path sourceDir = Paths.get("data");
Path targetDir = Paths.get("backup");
try (var stream = Files.list(sourceDir)) {
stream.forEach(path -> {
Path targetPath = targetDir.resolve(path.getFileName());
try {
Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Nüsxələndi: " + path.getFileName());
} catch (IOException e) {
System.out.println("Nüsxələmə xətası " + path.getFileName() + ": " + e.getMessage());
}
});
}
Bütün müvəqqəti faylların (*.tmp) silinməsi
try (var stream = Files.list(dir)) {
stream
.filter(path -> path.toString().endsWith(".tmp"))
.forEach(path -> {
try {
Files.delete(path);
System.out.println("Silindi: " + path.getFileName());
} catch (IOException e) {
System.out.println("Silmə xətası " + path.getFileName() + ": " + e.getMessage());
}
});
}
Path kolleksiyalarını emal etmək üçün Stream API-dən istifadə
try (var stream = Files.list(dir)) {
stream
.filter(path -> path.toString().endsWith(".txt"))
.sorted((p1, p2) -> {
try {
return Long.compare(Files.size(p2), Files.size(p1)); // ölçüyə görə, azalan
} catch (IOException e) {
return 0;
}
})
.limit(5) // yalnız ən böyük 5
.forEach(System.out::println);
}
4. Praktiki tapşırıqlar
Müəyyən tarixdən köhnə faylları tapmaq və silmək
Bir ildən çox dəyişdirilməmiş bütün faylları silək:
try (var stream = Files.list(dir)) {
stream
.filter(path -> {
try {
FileTime lastModified = Files.getLastModifiedTime(path);
return lastModified.toInstant().isBefore(Instant.now().minus(Duration.ofDays(365)));
} catch (IOException e) {
return false;
}
})
.forEach(path -> {
try {
Files.delete(path);
System.out.println("Silindi: " + path.getFileName());
} catch (IOException e) {
System.out.println("Silmə xətası " + path.getFileName() + ": " + e.getMessage());
}
});
}
Faylların şablona görə kütləvi ad dəyişdirilməsi
Tutaq ki, hər .txt-faylına prefiks "old_" əlavə etmək lazımdır:
try (var stream = Files.list(dir)) {
stream
.filter(path -> path.toString().endsWith(".txt"))
.forEach(path -> {
Path newPath = path.resolveSibling("old_" + path.getFileName());
try {
Files.move(path, newPath);
System.out.println("Adı dəyişdirildi: " + path.getFileName() + " -> " + newPath.getFileName());
} catch (IOException e) {
System.out.println("Ad dəyişdirmə xətası " + path.getFileName() + ": " + e.getMessage());
}
});
}
Bütün alt qovluqlardakı bütün faylların nüsxələnməsi (rekursiv)
Files.walk istifadə edərək, bütün alt qovluqlardakı faylları keçək:
try (var stream = Files.walk(sourceDir)) {
stream
.filter(Files::isRegularFile)
.forEach(path -> {
Path relative = sourceDir.relativize(path);
Path targetPath = targetDir.resolve(relative);
try {
Files.createDirectories(targetPath.getParent());
Files.copy(path, targetPath, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Nüsxələndi: " + path + " -> " + targetPath);
} catch (IOException e) {
System.out.println("Nüsxələmə xətası " + path + ": " + e.getMessage());
}
});
}
5. Vacib məqamlar və nüanslar
Kütləvi əməliyyatlarda xətaların emalı
Kütləvi əməliyyatlarda demək olar ki, həmişə “problemli” fayllar olur: giriş yoxdur, fayl məşğuldur, yol çox uzundur, fayl artıq mövcuddur və s. Əgər istisnaları dövr daxilində emal etməsəniz, proqram ilk xətada “çökəcək”. Buna görə də həmişə forEach daxilində try-catch istifadə edin ki, bir fayl üçün xətanı “udub” digərlərini emal edə biləsiniz.
Rekursiyanın performansa və çağırış stekinə təsiri
Böyük qovluqlar üçün Files.walk istifadə edərkən, əllə rekursiya etsəniz, keçid çox uzana bilər. Xoşbəxtlikdən, metodun özünün implementasiyası effektivdir və stekin daşmasına səbəb olmur. Amma qovluqları silmək/nüsxələmək üçün öz rekursiv funksiyanızı yazırsınızsa, ehtiyatlı olun: çox böyük iç-içə strukturlar üçün stek “daşa” bilər.
Böyük qovluqlarla iş
- Files.walk metodundan ehtiyatla istifadə edin: qovluqda minlərlə fayl varsa, bu, vaxt apara və çox yaddaş sərf edə bilər.
- Yalnız yuxarı səviyyəni emal etmək lazımdırsa — Files.list istifadə edin.
- Faylları maskaya görə tapmaq üçün ada görə filtrləmədən (endsWith, matches və s.) istifadə edin.
Nümunə: “Qovluğu müvəqqəti fayllardan təmizləmək”
Path tempDir = Paths.get("data/tmp");
try (var stream = Files.walk(tempDir)) {
stream
.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".tmp"))
.forEach(path -> {
try {
Files.delete(path);
System.out.println("Silindi: " + path);
} catch (IOException e) {
System.out.println("Silmə xətası " + path + ": " + e.getMessage());
}
});
}
Kütləvi əməliyyatlar üçün əsas metodlar
| Əməliyyat | Metod/yanaşma | Xüsusiyyətlər |
|---|---|---|
| Bütün faylları almaq | |
walk — rekursiv, list — yalnız yuxarı səviyyə |
| Uzantıya görə filtrləmə | |
Başqa filtrlərlə birləşdirmək olar |
| Fayllar qrupunun nüsxələnməsi | |
Hədəf qovluğun mövcudluğunu yoxlayın |
| Fayllar qrupunun silinməsi | |
Hər əməliyyat üçün try-catch istifadə etmək daha yaxşıdır |
| Kütləvi ad dəyişdirmə | |
Ad dəyişdirmə — yeni adla move əməliyyatıdır |
6. Kütləvi əməliyyatlarda tipik xətalar
Xəta №1: forEach daxilində işlənməyən istisnalar.
Çox yaygın problemdir: hər əməliyyatı try-catch ilə sarmasanız, proqram ilk problemli faylda çökəcək və qalanları emal olunmayacaq.
Xəta №2: Qovluğu fayl kimi silməyə/nüsxələməyə cəhd etmək (və ya əksinə).
Metodlar Files.delete və Files.copy fayl və qovluqlarla fərqli işləyir. Onları qarışdırmayın! Məsələn, boş olmayan qovluğu standart metodla silmək cəhdi xəta yaradacaq.
Xəta №3: Nüsxələmə zamanı hədəf yolun səhv qurulması.
Strukturu nəzərə almadan (məsələn, relativize istifadə etmədən) təyinat yolunu formalaşdırsanız, faylları üst-üstə yaza və ya arxivdə yanlış struktur ala bilərsiniz.
Xəta №4: Eyni anda həddindən artıq sayda fayl açmaq.
Dövr daxilində oxuma/yazma üçün axınlar açırsınızsa, onları bağlamağı unutmayın! Daha yaxşısı, try-with-resources istifadə edin.
Xəta №5: Dərinlik məhdudiyyəti olmadan rekursiv keçid.
Çox böyük və dərin qovluqlarda Files.walk istifadə etdikdə, performans və yaddaş problemləri yarana bilər. Dərinliyi məhdudlaşdırmaq lazımdırsa — dərinlik parametri olan overload-dan istifadə edin: Files.walk(dir, depth).
Xəta №6: Gizli/sistem fayllarını qeyri-ixtiyari nəzərə almamaq.
Tətbiq istifadəçi faylları ilə işləyirsə, təsadüfən gizli və ya sistem fayllarına toxuna bilərsiniz. Onları filtrləmək üçün Files.isHidden(path) metodundan istifadə edin.
GO TO FULL VERSION