CodeGym /Kurslar /JAVA 25 SELF /DataInputStream, DataOutputStream: primitivlərlə iş

DataInputStream, DataOutputStream: primitivlərlə iş

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

1. DataInputStream və DataOutputStream nə üçün lazımdır?

Fayllarla işləyərkən bəzən sırf mətn deyil, strukturlu məlumatları saxlamaq lazım olur: rəqəmlər, boolean dəyərlər, primitiv massivlər. Məsələn, sadə bir oyun yazdığınızı və istifadəçinin irəliləyişini saxlamaq istədiyinizi təsəvvür edin: xal sayı (int), cari səviyyə (int), oyun vaxtı (double), qalib statusu (boolean). Bunu əlbəttə mətn şəklində də yaza bilərsiniz:

12345
5
67.5
true

Amma bu, nə rahatdır, nə də təhlükəsizdir: sətirləri parse etmək lazımdır, format dəyişə və ya pozula bilər, həm də rəqəmlər daha çox yer tutur.

Fikir ondan ibarətdir ki, məlumatları fayla mətinə çevirmədən “xam” (ikili) şəkildə yazmaq. Bunun üçün Java-da iki əla sinif var:

  • DataOutputStream — primitiv tipləri axına yaza bilir.
  • DataInputStream — primitiv tipləri axından oxuya bilir.

Onlar adi bayt axınlarının (OutputStream/InputStream) üzərində işləyir. Yəni bunlar elə “üst qat”dır ki, təkcə bayt yazmırlar, həm də bu baytlardan int, double, boolean və hətta String yaratmağı bilirlər.

Bu necə işləyir?

Adi FileOutputStream-u elə bir konveyer kimi təsəvvür etmək olar ki, baytları bir-bir özünüz yerləşdirirsiniz. Tam ədəd və ya sətir yazmaq istəsəniz, hər elementin neçə bayt tutduğunu özünüz izləməlisiniz.

DataOutputStream işi asanlaşdırır: o, bu konveyerdə robot kimi davranır. Siz ona “rəqəmi yaz” və ya “sətiri yaz” deyirsiniz, o da məlumatları lazım olan sayda bayta qablaşdırıb diskə göndərir. Konveyerin digər ucunda həmin baytları geri orijinal obyektlərə yığmağı bacaran eyni cür robot — DataInputStream var.

Niyə bu rahatdır? Çünki int, double və ya boolean üçün neçə bayt lazım olduğunu düşünməyəcəksiniz. Məlumatlar yığcam saxlanır, sürətlə yazılıb oxunur və parse xətaları və ya format problemləri riski azalır.

2. Nümunə: primitivlərin yazılması və oxunması

Güman edək ki, (şərti) oyunumuzun nəticələrini saxlamaq istəyirik: oyunçunun adı (String), xal sayı (int), rekord vaxt (double), oyunçu qalib olubmu (boolean).

Məlumatların fayla yazılması

import java.io.*;

public class SaveGameData {
    public static void main(String[] args) {
        String fileName = "savegame.bin";
        String playerName = "Alice";
        int score = 12345;
        double recordTime = 67.5;
        boolean isWinner = true;

        try (DataOutputStream dos = new DataOutputStream(
                new FileOutputStream(fileName))) {
            dos.writeUTF(playerName);    // Sətiri yazırıq (UTF-8)
            dos.writeInt(score);         // int yazırıq (4 bayt)
            dos.writeDouble(recordTime); // double yazırıq (8 bayt)
            dos.writeBoolean(isWinner);  // boolean yazırıq (1 bayt)
            System.out.println("Məlumatlar fayla uğurla yazıldı!");
        } catch (IOException e) {
            System.out.println("Yazma xətası: " + e.getMessage());
        }
    }
}
  • writeUTF(String) — sətiri UTF-8 formatında yazır (başda uzunluqla).
  • writeInt(int)4 bayt yazır.
  • writeDouble(double)8 bayt yazır.
  • writeBoolean(boolean)1 bayt yazır (1 və ya 0).
  • Bütün metodlar məlumatları lazım olan formata avtomatik “qablaşdırır”.

Fayldan məlumatların oxunması

import java.io.*;

public class LoadGameData {
    public static void main(String[] args) {
        String fileName = "savegame.bin";

        try (DataInputStream dis = new DataInputStream(
                new FileInputStream(fileName))) {
            String playerName = dis.readUTF();      // Sətiri oxuyuruq
            int score = dis.readInt();              // int oxuyuruq
            double recordTime = dis.readDouble();   // double oxuyuruq
            boolean isWinner = dis.readBoolean();   // boolean oxuyuruq

            System.out.println("Oyunçunun adı: " + playerName);
            System.out.println("Xallar: " + score);
            System.out.println("Vaxt: " + recordTime);
            System.out.println("Qalib: " + isWinner);
        } catch (IOException e) {
            System.out.println("Oxuma xətası: " + e.getMessage());
        }
    }
}

Vacib məqam:
Oxuma ardıcıllığı yazma ardıcıllığı ilə eyni olmalıdır! Əgər əvvəl sətir, sonra int, sonra double yazmısınızsa — eyni ardıcıllıqla oxuyun. Əks halda səhv və ya “qarışıq” məlumat alacaqsınız.

3. Hansı tiplər dəstəklənir?

DataOutputStreamDataInputStream Java-nın bütün əsas primitiv tiplərini dəstəkləyir:

Yazma metodu Oxuma metodu Məlumat tipi Ölçü (bayt)
writeBoolean(boolean)
readBoolean()
boolean 1
writeByte(int)
readByte()
byte 1
writeShort(int)
readShort()
short 2
writeChar(int)
readChar()
char 2
writeInt(int)
readInt()
int 4
writeLong(long)
readLong()
long 8
writeFloat(float)
readFloat()
float 4
writeDouble(double)
readDouble()
double 8
writeUTF(String)
readUTF()
String (UTF) dəyişkən

Qeydlər:
- Sətirlər üçün adətən writeUTF/readUTF istifadə olunur (başda sətirin uzunluğu və sonra UTF-8-də baytlar yazılır).
- Massivi yazmaq istəyirsinizsə, əvvəlcə onun uzunluğunu, sonra isə elementləri bir-bir yazın.

4. İrəliləmiş nümunə: primitiv massivləri saxlamaq

Massivin yazılması

int[] scores = {100, 200, 300, 400, 500};
try (DataOutputStream dos = new DataOutputStream(
        new FileOutputStream("scores.bin"))) {
    dos.writeInt(scores.length); // Əvvəlcə massivın uzunluğunu yazırıq
    for (int score : scores) {
        dos.writeInt(score);     // Sonra isə hər elementi
    }
}

Massivin oxunması

try (DataInputStream dis = new DataInputStream(
        new FileInputStream("scores.bin"))) {
    int length = dis.readInt();      // Uzunluğu oxuyuruq
    int[] scores = new int[length];
    for (int i = 0; i < length; i++) {
        scores[i] = dis.readInt();   // Elementləri oxuyuruq
    }
    // Massivi çap edirik
    for (int score : scores) {
        System.out.println(score);
    }
}

Niyə əvvəlcə uzunluq?
Çünki oxuyarkən neçə ədəd yazıldığını bilmirik. Başda uzunluğu yazmaqla fayl formatını özünü sənədləşdirən edirik.

5. Vacib nüanslar və xüsusiyyətlər

DataInputStream/DataOutputStream nə vaxt istifadə olunmalıdır?

  • Primitivlərdən ibarət strukturlu məlumatları saxlamaq/yükləmək lazım olanda.
  • Java-da yazılmış proqramlar arasında ikili məlumat mübadiləsi üçün (hətta formatı bilirsinizsə, müxtəlif dillər arasında da).
  • Yığcamlıq və sürət vacib olanda (məsələn, loglar, hesablamaların nəticələri, böyük ədəd massivləri).

Nə vaxt istifadə etməmək lazımdır:

  • Əgər insan üçün oxunaqlı format lazımdırsa (CSV, JSON, XML) — mətn formatlarından istifadə edin.
  • Daxili qurulu mürəkkəb obyektlər üçün — ObjectOutputStream/ObjectInputStream vasitəsilə seriyalaşdırmadan istifadə etmək daha yaxşıdır (bu ayrıca mövzudur).

Bufferləşdirmə

DataOutputStreamDataInputStream öz-özlüyündə məlumatları bufferləşdirmir. Böyük fayllarla işləyərkən performansı artırmaq istəyirsinizsə, onları BufferedOutputStream/BufferedInputStream ilə qablaşdırın:

try (DataOutputStream dos = new DataOutputStream(
        new BufferedOutputStream(new FileOutputStream("data.bin")))) {
    // ...
}

Sətirlərin kodlaşdırılması

Metodlar writeUTF/readUTF xüsusi formatdan istifadə edir: əvvəlcə sətirin uzunluğu (baytlarda) yazılır, sonra isə məzmunu UTF-8-də. Bunu sadəcə bayt massivinin yazılması ilə qarışdırmayın!

İstisnalar

Oxuma/yazma əməliyyatları IOException ata bilər, əgər fayl əlçatan deyil, zədələnib və ya vaxtından əvvəl başa çatıbsa. Yazılandan artıq oxumağa cəhd etdikdə çox vaxt EOFException gəlir. Həmişə try-with-resources konstruksiyasından istifadə edin və ya istisnaları try-catch ilə emal edin.

Oxu və yazma ardıcıllığı

Ən yayğın səhv — oxuma və yazma ardıcıllığının uyğun gəlməməsidir. Əgər belə yazmısınızsa: int, double, boolean, amma oxuyursunuz: double, int, boolean — səhv məlumat və ya istisna alacaqsınız.

6. Tipik səhvlər

Xəta № 1: Yazma və oxuma ardıcıllığının pozulması. Metodların ardıcıllığını dəyişsəniz, məlumatlar səhv oxunacaq və ya istisna atılacaq. Məsələn, əvvəlcə sətir, sonra rəqəm yazmısınız, amma oxuyarkən əvvəlcə rəqəmi oxumağa cəhd edirsiniz — format xətası alacaqsınız.

Xəta № 2: Massivin uzunluğunu yazmağı unutmusunuz. Primitiv massivini yazırsınızsa, lakin onun uzunluğunu yazmamısınızsa, oxuyarkən neçə elementi oxumaq lazım olduğunu bilməyəcəksiniz. Bu ya səhvə, ya da sonda “artıq” məlumatların çıxmasına gətirib çıxarır.

Xəta № 3: Faylın sonundan sonra oxumağa cəhd. Yazılandan çox məlumat oxumağa çalışsanız, EOFException (end of file) alacaqsınız.

Xəta № 4: Mətn faylları üçün DataInputStream/DataOutputStream istifadə etmək. Bu siniflər, məsələn, Notepad-da yaradılmış adi mətn fayllarını oxumaq üçün nəzərdə tutulmayıb. Belə faylı readInt() ilə oxumağa çalışsanız — mənasız məlumat və ya səhv alacaqsınız.

Xəta № 5: Axını bağlamamısınız. Əgər try-with-resources istifadə etmirsinizsə və ya axınları əl ilə bağlamırsınızsa, fayl digər proqramlar üçün əlçatan olmaya bilər və ya dəyişikliklər tam yazılmaya bilər.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION