CodeGym /Kurslar /JAVA 25 SELF /Fayllar üzərində kütləvi əməliyyatlar

Fayllar üzərində kütləvi əməliyyatlar

JAVA 25 SELF
Səviyyə, Dərs
Mövcuddur

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
Files.list
Yalnız cari qovluğun məzmunu
Files.walk
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
Files.list, Files.walk
walk — rekursiv, list — yalnız yuxarı səviyyə
Uzantıya görə filtrləmə
filter(path -> ...)
Başqa filtrlərlə birləşdirmək olar
Fayllar qrupunun nüsxələnməsi
forEach + Files.copy
Hədəf qovluğun mövcudluğunu yoxlayın
Fayllar qrupunun silinməsi
forEach + Files.delete
Hər əməliyyat üçün try-catch istifadə etmək daha yaxşıdır
Kütləvi ad dəyişdirmə
forEach + Files.move
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.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Kritik məlumatların ehtiyat nüsxəsinin yaradılması 💾
Kritik məlumatların ehtiyat nüsxəsinin yaradılması 💾
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Rəqəmsal kitabxanaçı: mətn xəzinələrinin toplanması 📚
Rəqəmsal kitabxanaçı: mətn xəzinələrinin toplanması 📚
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION