Čítanie/Zápis Bytov a Zo Súboru Iba Pomocou Java.IO

0

Otázka

Ako môžeme napísať byte pole do súboru (a prečítajte si ju späť od tohto súboru) v Jave?

Áno, všetci vieme, existuje už veľa otázok, ako že, ale že si veľmi chaotický a subjektívne vzhľadom na skutočnosť, že existuje mnoho spôsobov, ako na splnenie týchto úloh.

Tak poďme znížiť rozsah otázka:

Domény:

  • Android / Java

To, čo chceme:

  • Rýchlo (ako je to možné)
  • Bug-free (v pevne starostlivý spôsob)

To, čo nie sme robiť:

  • Knižníc tretích strán
  • Všetky knižnice, ktoré si vyžadujú Android API neskôr ako 23 (Ibištek)

(Tak, že sa vylučuje Apache Commons, Google Guava, Java.nioa necháva nás dobrý ol' Java.io)

Čo budeme potrebovať:

  • Bajt poľa je vždy presne rovnaký (obsah a veľkosť) potom, čo šiel cez napísať-potom-čítať procesu
  • Napíšte metóda vyžaduje iba dva argumenty: Súbor súbor, a byte[] data
  • Čítať metóda vracia byte[] a vyžaduje iba jeden argument: Súbor súbor

V mojom konkrétnom prípade, tieto metódy sú súkromné (nie knižnicu) a NIE je zodpovedný za nasledujúce, (ale ak chcete vytvoriť viac univerzálne riešenie, ktoré sa týka širšieho publika, ísť na to):

  • Vlákno-bezpečnosť (súbor nebude možné získať prístup do viac ako jeden proces naraz)
  • Súbor je null
  • Súbor ukážte ukazovateľom na neexistujúce miesto
  • Nedostatok povolenia na umiestnenie súboru
  • Bajt poľa je príliš veľký
  • Bajt poľa je null
  • Rokovaní s "index" "dĺžka" alebo "pridať" argumenty/spôsobilostí

Takže... sme akési hľadanie konečné nepriestrelné kód že ľudia v budúcnosti možno predpokladať, je bezpečné používať, pretože vaša odpoveď má veľa hore-hlasov, a tam sú žiadne komentáre, ktoré hovoria, "Že by mohol zlyhať, ak..."

To je to, čo som doteraz:

Napíšte Bajtov Do Súboru:

private void writeBytesToFile(final File file, final byte[] data) {
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data);
            fos.close();
        } catch (Exception e) {
            Log.i("XXX", "BUG: " + e);
        }
    }

Prečítajte Si Bajtov Zo Súboru:

private byte[] readBytesFromFile(final File file) {
        RandomAccessFile raf;
        byte[] bytesToReturn = new byte[(int) file.length()];
        try {
            raf = new RandomAccessFile(file, "r");
            raf.readFully(bytesToReturn);
        } catch (Exception e) {
            Log.i("XXX", "BUG: " + e);
        }
        return bytesToReturn;
}

Z toho, čo som čítal, že prípadné Výnimky sú:

FileNotFoundException : som správne, že toto by sa nemalo stať, tak dlho, ako cestu k súboru sa dodáva bola odvodená pomocou Android vlastné interné nástroje a/alebo ak aplikácia bola testovaná správne?

IOException : ja naozaj neviem, čo by mohlo spôsobiť túto... ale ja som za predpokladu, že neexistuje žiadny spôsob, ako okolo neho, ak to robí.

Takže s tým na mysli... môžete tieto metódy sa zlepšiť alebo nahradiť, a ak áno, čo?

android arrays file java
2021-11-23 02:58:43
2

Najlepšiu odpoveď

6

Zdá sa, že tieto budú core utility/library metódy, ktoré musia fungovať na Android API 23 alebo neskôr.

O knižnici metódy, zistil som, že je najlepšie, aby žiadne predpoklady o tom, ako aplikácie budú využívať tieto metódy. V niektorých prípadoch môžu žiadosti chcete dostávať kontrolovať IOExceptions (pretože údaje zo súboru musí existovať pre aplikácie na prácu), v iných prípadoch aplikácie môžu ani starostlivosť, ak nie sú k dispozícii údaje (pretože údaje zo súboru je len pamäť, ktorá je k dispozícii aj z primárneho zdroja).

Keď príde na I/O operácie, nikdy nie je záruka, že operácie budú úspešné (napr. užívateľ pád telefónu na wc). Knižnice by sa mali zamyslieť a dať žiadosť na výber ako zvládnuť chyby.

Ak chcete optimalizovať I/O výkon vždy predpokladať "happy cesta" a chytiť chyby, zistiť, čo sa pokazilo. Toto je počítadlo intuitívne do normálneho programovania, ale nevyhnutné pri riešení skladovanie I/O. napríklad, len kontrola, ak súbor existuje, skôr než čítanie zo súboru môžete vytvoriť svoj aplikácia dvakrát tak pomaly - všetky tieto druhy I/O akcie pridať rýchlo hore na pomaly sa aplikácia nadol. Len predpokladať súbor existuje a ak dostanete chybu, len potom skontrolujte, či súbor existuje.

Tak vzhľadom tie myšlienky, hlavné funkcie môže vyzerať takto:

public static void writeFile(File f, byte[] data) throws FileNotFoundException, IOException {
    try (FileOutputStream out = new FileOutputStream(f)) {
        out.write(data);
    }
}

public static int readFile(File f, byte[] data) throws FileNotFoundException, IOException {
    try (FileInputStream in = new FileInputStream(f)) {
        return in.read(data); 
    }
}

Poznámky o implementácii:

  • Metódy, môžete tiež hodiť runtime-výnimky ako NullPointerExceptions - tieto metódy sú nikdy nebude "bez chýb".
  • Nemyslím si, že vyrovnávacej pamäte je potrebné/wanted v metód vyššie, pretože iba jeden rodák hovor je hotovo (pozri aj tu).
  • Aplikácia teraz má tiež možnosť čítať len začiatok súboru.

Aby to bolo jednoduchšie pre aplikáciu na čítanie súboru, ďalšie metódy môžu byť pridané. Ale všimnite si, že to je až do knižnice, aby zistil všetky chyby a správa ich žiadosť, keďže samotnú aplikáciu si už detekciu týchto chýb.

public static byte[] readFile(File f) throws FileNotFoundException, IOException {
    int fsize = verifyFileSize(f);
    byte[] data = new byte[fsize];
    int read = readFile(f, data);
    verifyAllDataRead(f, data, read);
    return data;
}

private static int verifyFileSize(File f) throws IOException {
    long fsize = f.length();
    if (fsize > Integer.MAX_VALUE) {
        throw new IOException("File size (" + fsize + " bytes) for " + f.getName() + " too large.");
    }
    return (int) fsize;
}

public static void verifyAllDataRead(File f, byte[] data, int read) throws IOException {
    if (read != data.length) {
        throw new IOException("Expected to read " + data.length 
                + " bytes from file " + f.getName() + " but got only " + read + " bytes from file.");
    }
}

Táto implementácia pridáva ďalší skrytý bod zlyhania: OutOfMemory v bode, kde sa nové dátové pole je vytvorené.

Ubytovať aplikácie, ďalšie, ďalšie metódy môžu byť pridané pomôcť s rôznymi scenár je. Napríklad, povedzme, že aplikácia je naozaj nechce zaoberať skontrolovať výnimky:

public static void writeFileData(File f, byte[] data) {
    try {
        writeFile(f, data);
    } catch (Exception e) {
        fileExceptionToRuntime(e);
    }
}

public static byte[] readFileData(File f) {
    try {
        return readFile(f);
    } catch (Exception e) {
        fileExceptionToRuntime(e);
    }
    return null;
}

public static int readFileData(File f, byte[] data) {
    try {
        return readFile(f, data);
    } catch (Exception e) {
        fileExceptionToRuntime(e);
    }
    return -1;
}

private static void fileExceptionToRuntime(Exception e) {
    if (e instanceof RuntimeException) { // e.g. NullPointerException
        throw (RuntimeException)e;
    }
    RuntimeException re = new RuntimeException(e.toString());
    re.setStackTrace(e.getStackTrace());
    throw re;
}

Metóda fileExceptionToRuntime je minimálna realizácia, ale ukazuje nápad tu.

Knižnice by mohli pomáhať aj aplikáciu do problémov, keď sa chyba vyskytne. Napríklad metóda canReadFile(File f) môže skontrolovať, či súbor existuje a je čitateľný a nie je príliš veľký. Aplikácia môže volať takúto funkciu po súbor-čítať zlyhá a skontrolujte bežných dôvodov, prečo súbor sa nedá čítať. To isté sa dá urobiť pre zápis do súboru.

2021-11-28 22:59:55

Oceniť užitočné a poučné odpoveď. Som dáva ju dohromady v projekte a zistiť, či dokážem pochopiť to lepšie. Aký je dôvod pre zmenu readBytes metóda podpis z toho, čo som mal? (tvoje trvá byte[] ako jeden z args a vráti int). Tiež je váš posledný blok kódu, ktorý je určený na časť knižnice alebo aplikáciu?
Nerdy Bunz

tiež nebude riadok "vrátiť (int), f.dĺžka();" crash od f.dĺžka je väčšia ako Celé číslo.MAX_VALUE?
Nerdy Bunz

@NerdyBunz O poslednú otázku: nie, "downcasting" nedáva chybu a v tomto prípade IOException je hodená, keď fsize hodnota je príliš veľké. Tiež, mal by som mať znovu použité fsize tam (od f.length() výsledky v I/O operácie).
vanOekel

O prvej otázke: všetky je určený na časť knižnice. Môj byte[] readFile(File f) je podobným byte[] readBytesFromFile(final File file). Môj byte[] readFileData(File f) metóda je príklad toho, ako si môžete prispôsobiť tieto funkcie sa ďalej. Mal som problém zistiť, ktoré metódy na odhalenie (public) a udržať skryté (private) a myslím si, že je len otázka môžete odpoveď: metódy, ktoré chcete, aby aplikácia mohla používať bez toho, aby reštriktívne k žiadosti?
vanOekel
3

Hoci nie je možné použiť tretej strany, knižnice, ešte stále sa môžete si ich kód a poučiť sa z ich skúseností. V Google Guava napríklad, zvyčajne prečítajte si súbor, do bajtov ako je tento:

FileInputStream reader = new FileInputStream("test.txt");
byte[] result = ByteStreams.toByteArray(reader);

Jadro vykonávanie to je toByteArrayInternal. Pred zavolaním to, by ste mali skontrolovať:

  • Nie null súbor sa absolvoval (NullPointerException)
  • Súbor existuje (FileNotFoundException)

Po tom, to je znížená na spracovanie InputStream a to, kde IOExceptions pochádzajú. Pri čítaní prúdy veľa vecí mimo kontrolu vašej žiadosti sa môže pokaziť (chybné sektory a iné hardvérové problémy, mal-fungovaní ovládače, OS prístupové práva) a prejavujú s IOException.

Ja som kopírovanie tu realizácie:

private static final int BUFFER_SIZE = 8192;

/** Max array length on JVM. */
private static final int MAX_ARRAY_LEN = Integer.MAX_VALUE - 8;

private static byte[] toByteArrayInternal(InputStream in, Queue<byte[]> bufs, int totalLen)
      throws IOException {
    // Starting with an 8k buffer, double the size of each successive buffer. Buffers are retained
    // in a deque so that there's no copying between buffers while reading and so all of the bytes
    // in each new allocated buffer are available for reading from the stream.
    for (int bufSize = BUFFER_SIZE;
        totalLen < MAX_ARRAY_LEN;
        bufSize = IntMath.saturatedMultiply(bufSize, 2)) {
      byte[] buf = new byte[Math.min(bufSize, MAX_ARRAY_LEN - totalLen)];
      bufs.add(buf);
      int off = 0;
      while (off < buf.length) {
        // always OK to fill buf; its size plus the rest of bufs is never more than MAX_ARRAY_LEN
        int r = in.read(buf, off, buf.length - off);
        if (r == -1) {
          return combineBuffers(bufs, totalLen);
        }
        off += r;
        totalLen += r;
      }
    }

    // read MAX_ARRAY_LEN bytes without seeing end of stream
    if (in.read() == -1) {
      // oh, there's the end of the stream
      return combineBuffers(bufs, MAX_ARRAY_LEN);
    } else {
      throw new OutOfMemoryError("input is too large to fit in a byte array");
    }
  }

Ako vidíte, väčšina z logiky má čo do činenia s čítanie súboru na kusy. Je to zvládnuť situácie, keď neviete veľkosť InputStream, pred začatím čítania. Vo vašom prípade, musíte len na čítanie súborov a vy by ste mali byť schopní vedieť, dĺžka vopred, tak táto zložitosť sa mohli vyhnúť.

Druhú kontrolu je OutOfMemoryException. V štandardných Java limit je príliš veľká, avšak v Android, bude to oveľa menšiu hodnotu. Mali by ste skontrolovať, skôr sa snaží čítať súbor, ktorý nie je dostatok pamäte.

2021-11-26 13:42:23

V iných jazykoch

Táto stránka je v iných jazykoch

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................