1. Faylı sətir-sətir oxuma: BufferedReader və digərləri
Əvvəllər bayt-bayt oxuma ilə tanış olmuşduq: bu, binar formatlarla işləmək üçün rahatdır, lakin mətn üçün əlverişli deyil. Fayl simvollardan ibarətdir və onlar kodlaşdırmadan asılıdır. Buna görə də Java mətnlə iş üçün rahat üst qatmanlar təklif edir — FileReader, BufferedReader və digər siniflər, hansı ki, bayt axınını simvol və sətir axınına çevirir.
Təsəvvür edin, mətn faylı — istər proqramın iş loqu, istər istifadəçi siyahısı, hətta böyük bir roman “Voyna i mir”. Bəzən bütün faylı sürətlə tam oxumaq, bəzən — onu sətir-sətir keçmək, bəzən isə — konkret bir sətiri çıxarmaq lazım olur.
Java-da bunu etmək üçün bir neçə üsul var və seçim faylın ölçüsündən və tapşırıqdan asılıdır. Faylı sətir-sətir emal etmək lazımdırsa (məsələn, sətir sayını hesablamaq və ya bir yazını tapmaq), sətir-sətir oxumadan istifadə olunur. Əgər fayl kiçikdirsə — onu yaddaşa tam yükləyib sətirlər siyahısı kimi işləmək olar.
Niyə sətir-sətir oxumaq yaxşıdır?
Böyük fayllar üçün sətir-sətir oxuma yaddaş problemlərindən xilas edir. Giqabaytlıq loqu yaddaşa yükləmək yaxşı fikir deyil: OutOfMemoryError almaq riski böyükdür. Amma faylı sətir-sətir oxumaq xüsusi xərclər olmadan mümkündür, hətta o, yüzlərlə meqabayt olsa belə.
Bu Java-da necə edilir?
Ən klassik üsul — BufferedReader (və ya onun yaxınlarını) və readLine() metodunu istifadə etməkdir.
import java.io.*;
public class ReadLinesDemo {
public static void main(String[] args) {
String fileName = "example.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
System.out.printf("%3d: %s%n", lineNumber, line);
lineNumber++;
}
} catch (IOException e) {
System.out.println("Faylı oxuyarkən xəta: " + e.getMessage());
}
}
}
Burada faylı oxuma üçün açırıq və readLine() vasitəsilə sətir-sətir oxuyuruq, sonuna çatana qədər (null). Sonra hər sətiri nömrəsi ilə birlikdə çıxarırıq.
Qısaca try-with-resources haqqında
try (...) { ... } konstruksiyasını görürsünüz? Bu, try-with-resources-dir. O, oxuma ortasında xəta baş versə belə, faylın bağlanacağını təmin edir. Sizə close() metodunu əl ilə çağırmaq lazım deyil: bağlama avtomatik baş verəcək, hətta catch/finally zamanı belə.
BufferedReader nə üçündür?
BufferedReader tək-tək simvol yox, bütöv bloklarla (adətən 8192 bayt) oxuyur ki, bu da fayllarla işi sürətləndirir. Bundan başqa, onda readLine() kimi rahat metod var ki, sətiri yeni sətir simvoluna qədər dərhal qaytarır.
Hansı buferi seçmək lazımdır?
Adətən BufferedReader 8192 bayt ölçülü buferdən (8 KB) istifadə edir — bu, əksər tapşırıqlar üçün kifayətdir. Əgər çox uzun sətirlər oxuyursunuzsa (məsələn, 100_000 simvol), buferi böyütmək olar:
BufferedReader reader = new BufferedReader(new FileReader(fileName), 65536); // 64 KB bufer
Amma adi tapşırıqlar üçün standart ölçü əladır.
2. Faylı tam oxuma: Files.readAllLines və Files.readString
Əgər fayl kiçikdirsə (məsələn, 10–20 MB-ə qədər), onu tam yükləmək rahatdır. Məsələn, bütün sətirlərin siyahısını tez almaq, mətni analiz etmək və ya onu şəbəkə ilə göndərmək lazım olduqda.
Müasir üsul: Files.readAllLines
Java 7-dən etibarən çox faydalı metodları olan əlverişli Files sinfi peyda olub.
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class ReadAllLinesDemo {
public static void main(String[] args) {
Path path = Path.of("example.txt");
try {
List<String> lines = Files.readAllLines(path);
for (int i = 0; i < lines.size(); i++) {
System.out.printf("%3d: %s%n", i + 1, lines.get(i));
}
} catch (IOException e) {
System.out.println("Faylı oxuyarkən xəta: " + e.getMessage());
}
}
}
Burada Files.readAllLines(path) metodu sətirlərin siyahısını (List<String>) qaytarır. Bu siyahı ilə adi kolleksiya kimi işləmək olar: axtarmaq, çeşidləmək, filtrləmək.
Daha da müasir: Files.readString (Java 11+)
Əgər bütün faylı tək bir sətrə (məsələn, alt-sətir axtarışı və ya JSON-a ötürmə üçün) almaq lazımdırsa, Files.readString istifadə edin:
import java.nio.file.*;
import java.io.IOException;
public class ReadStringDemo {
public static void main(String[] args) {
Path path = Path.of("example.txt");
try {
String content = Files.readString(path);
System.out.println("Faylın məzmunu:");
System.out.println(content);
} catch (IOException e) {
System.out.println("Faylı oxuyarkən xəta: " + e.getMessage());
}
}
}
Bəs kodlaşdırma necə olsun?
Defolt olaraq Files.readAllLines və Files.readString metodları sisteminizin standart kodlaşdırmasından istifadə edir. Əgər fayl başqa kodlaşdırmada yazılıbsa (məsələn, Windows-1251), onu açıq şəkildə göstərin:
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
String content = Files.readString(path, StandardCharsets.UTF_8);
3. Yanaşmaların müqayisəsi: hansını nə vaxt istifadə etməli
| Yanaşma | Nə vaxt istifadə etməli | Üstünlüklər | Çatışmazlıqlar |
|---|---|---|---|
|
Böyük fayllar, sətir-sətir emal | Yaddaşa qənaət edir, çevik | Yalnız sətir üzrə oxu |
|
Kiçik və orta ölçülü fayllar | Dərhal sətirlər siyahısı, sadə | Böyük fayllar üçün — OutOfMemory |
|
Kiçik fayllar, mətnin hamısı lazımdır | Bütün mətn bütöv | Sətirlərə bölünmə yoxdur |
Tövsiyə:
— Fayl kiçikdirsə — Files.readAllLines və ya Files.readString istifadə edin.
— Fayl böyükdürsə və ya ölçüsünü bilmirsinizsə — BufferedReader.readLine() istifadə edin.
4. Praktiki tapşırıqlar: nümunələr və izah
Nümunə 1. Böyük faylda sətirlərin sayılması
Tutaq ki, böyük bir faylda neçə sətir olduğunu bilmək lazımdır (məsələn, server loqu).
import java.io.*;
public class LineCount {
public static void main(String[] args) {
String fileName = "biglog.txt";
int lineCount = 0;
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
while (reader.readLine() != null) {
lineCount++;
}
System.out.println("Faylda sətirlərin sayı: " + lineCount);
} catch (IOException e) {
System.out.println("Faylı oxuyarkən xəta: " + e.getMessage());
}
}
}
Nümunə 2. Məzmun üzrə sətir axtarışı
“xəta” sözünün keçdiyi bütün sətirləri axtaraq (case-insensitive):
import java.io.*;
public class FindErrorLines {
public static void main(String[] args) {
String fileName = "biglog.txt";
String keyword = "xəta";
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
if (line.toLowerCase().contains(keyword)) {
System.out.printf("%3d: %s%n", lineNumber, line);
}
lineNumber++;
}
} catch (IOException e) {
System.out.println("Faylı oxuyarkən xəta: " + e.getMessage());
}
}
}
Nümunə 3. Kiçik fayldan konfiqurasiyanın yüklənməsi
config.txt faylı:
host=localhost
port=8080
mode=dev
Onu tam oxuyuruq və açar-dəyər cütlərinə bölürük:
import java.nio.file.*;
import java.util.*;
public class ConfigLoader {
public static void main(String[] args) throws Exception {
Path path = Path.of("config.txt");
List<String> lines = Files.readAllLines(path);
Map<String, String> config = new HashMap<>();
for (String line : lines) {
if (line.trim().isEmpty() || line.startsWith("#")) continue; // boş sətirləri və şərhləri keçirik
String[] parts = line.split("=", 2);
if (parts.length == 2) {
config.put(parts[0].trim(), parts[1].trim());
}
}
System.out.println("Yüklənmiş konfiqurasiya: " + config);
}
}
5. Mətn fayllarını oxuyarkən tipik səhvlər
Xəta №1: Binar faylı mətn kimi oxumağa cəhd. Əgər şəkil və ya arxivi BufferedReader və ya Files.readAllLines ilə açsanız, mənasız simvollar və OutOfMemoryError riski alacaqsınız. Binar fayllar üçün InputStream istifadə edin!
Xəta №2: İstisnanı emal etmədiniz. Fayllar silinə, köçürülə, bloklana bilər. Həmişə oxunu try-catch ilə əhatə edin və istifadəçini problemlər barədə məlumatlandırın.
Xəta №3: Kodlaşdırmanı nəzərə almamaq. Əgər mətn rus dilindədirsə və siz faylı kodlaşdırmanı göstərmədən oxuyursunuzsa, “?????” ala bilərsiniz. StandardCharsets.UTF_8 və ya sizə lazım olan kodlaşdırmadan istifadə edin.
Xəta №4: Axını bağlamağı unutmaq. Əgər fayl bağlanmasa, proqram bitənə qədər bloklanmış qala bilər. Həmişə try-with-resources istifadə edin.
Xəta №5: Mətn faylları üçün read() əvəzinə readLine() istifadə etmək. read() metodu simvol-simvol oxuyur — bu, sətirlər üçün həm yavaşdır, həm də əlverişsizdir. Mətn üçün readLine() və ya Files sinfinin metodlarından istifadə edin.
GO TO FULL VERSION