Citire/Scriere de Bytes și Dintr-un Fișier Folosind Doar Java.IO

0

Problema

Cum putem scrie un octet matrice într-un fișier (și citește din fișierul respectiv) în Java?

Da, știm cu toții, există deja o mulțime de întrebări de genul ăsta, dar ei a lua foarte murdar și subiective, datorită faptului că există atât de multe moduri de a realiza această sarcină.

Așa că hai să reducă domeniul de aplicare de întrebarea:

Domeniu:

  • Android / Java

Ceea ce ne dorim:

  • Repede (posibil)
  • Bug-free (într-un mod rigid modul meticulos)

Ce nu facem:

  • Biblioteci terțe părți
  • Orice biblioteci care necesită API Android mai târziu de 23 (Marshmallow)

(Deci, care exclude Apache Commons, Google Guava, Java.nio, și ne lasă cu bun ol' Java.io)

De ce avem nevoie:

  • Matrice octet este întotdeauna exact la fel (conținutul și mărimea) după parcurgerea scrie-apoi-a citit procesul
  • Scrie metodă necesită doar două argumente: File, și byte[] date
  • Citește metoda returnează un byte[] și necesită doar un singur argument: File

In cazul meu particular, aceste metode sunt private (nu o bibliotecă) și sunt responsabili pentru următoarele, (dar dacă doriți pentru a crea o soluție universală, care se aplică la un public mai larg, du-te pentru ea):

  • Fir de siguranță-(fișierul nu va fi accesate de mai mult de un proces, la o dată)
  • Dosar fiind nul
  • Fișier arătând spre inexistente locație
  • Lipsa de permisiuni la locația fișierului
  • Matrice octet fiind prea mare
  • Matrice octet fiind nul
  • De-a face cu orice "index", "lungime" sau "adăugare" argumente/capacități

Deci... suntem un fel de în căutare de definitive bullet-dovada de cod pe care oamenii, în viitor, se poate presupune că este sigur de utilizat, deoarece răspunsul tău are o mulțime de voturi și nu există comentarii care spun, "Care s-ar putea prăbuși dacă..."

Aceasta este ceea ce am pana acum:

Scrie Octeți Într-Un Fișier:

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);
        }
    }

Citește Octeți Din Fișier:

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;
}

Din ce am citit, posibilele Excepții sunt:

FileNotFoundException : Sunt corect ca acest lucru să nu se întâmple atâta timp cât calea de fișier de a fi livrate a fost derivat cu ajutorul Android propriile instrumente interne și/sau în cazul în care aplicația a fost testat în mod corespunzător?

IOException : chiar nu stiu ce ar putea cauza asta... dar presupun că nu există nici o cale în jurul valorii de ea dacă o face.

Deci, cu asta în minte... aceste metode pot fi îmbunătățite sau înlocuite, și dacă da, cu ce?

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

Cel mai bun răspuns

6

Se pare ca acestea sunt de gând să fie de bază utilitate/biblioteca metode care trebuie să ruleze pe Android API 23 sau mai târziu.

Privind biblioteca metode, mi se pare cel mai bun de a face presupuneri pe cât de aplicații vor utiliza aceste metode. În unele cazuri, cererile, poate doriți să primiți verificat IOExceptions (deoarece datele dintr-un fișier trebuie să existe pentru aplicarea la locul de muncă), în alte cazuri, cererile, poate nici nu-mi pasă dacă nu sunt disponibile date (deoarece datele dintr-un fișier este doar cache-ul, care este, de asemenea, disponibil de la o sursă primară).

Când e vorba de operațiuni de I/O, nu există niciodată o garanție că operațiunile vor reuși (de exemplu, utilizatorul scadere telefonul în toaletă). Biblioteca ar trebui să reflecte asta si da aplicația o alegere cu privire la modul să se ocupe de erori.

Pentru a optimiza I/O performanță presupune întotdeauna "fericit calea" și a prinde erori să-mi dau seama ce a mers prost. Acest lucru este contra-intuitiv la normal de programare, dar esențial, în care se ocupă cu depozitarea I/O. De exemplu, doar a verifica dacă există un fișier înainte de a citi dintr-un fișier se poate face cererea dumneavoastră de două ori la fel de lent - toate aceste tipuri de I/O acțiuni adăuga până rapid la lent cererea dumneavoastră jos. Să presupunem fișierul există și dacă primiți o eroare, doar apoi verificați dacă fișierul există.

Astfel, având aceste idei, principalele funcții ar putea arata ca:

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); 
    }
}

Note despre aplicare:

  • Metodele pot arunca, de asemenea, runtime-excepții, cum ar fi NullPointerExceptions - aceste metode nu sunt niciodată de gând să fie "bug free".
  • Nu cred tamponare este nevoie de/a vrut în metodele de mai sus, deoarece numai un nativ apelul este făcut (a se vedea, de asemenea, aici).
  • Aplicația are, de asemenea, opțiunea de a citi doar începutul unui fișier.

Pentru a face mai ușor pentru o aplicație pentru a citi un fișier, o metodă suplimentară poate fi adăugată. Dar, rețineți că acesta este de până la bibliotecă pentru a detecta eventualele erori și să le raporteze la cerere de la aplicarea în sine nu mai poate detecta aceste erori.

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.");
    }
}

Această punere în aplicare, adaugă un alt ascunse punct de eșec: OutOfMemory la punctul în cazul în care cea mai nouă gamă de date este creat.

Pentru a găzdui aplicații suplimentare, metode suplimentare pot fi adăugate pentru a ajuta cu diferite scenarii. De exemplu, să spunem că aplicarea într-adevăr nu doresc să se ocupe cu verificat excepții:

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;
}

Metoda fileExceptionToRuntime este un minim de implementare, dar arată ideea aici.

Biblioteca ar putea ajuta, de asemenea, o aplicație pentru a depana atunci când o eroare apare. De exemplu, o metodă canReadFile(File f) putea verifica dacă un fișier există și este ușor de citit și nu este prea mare. Cererea ar putea apela la un astfel de funcție, după ce un fișier citit eșuează și a verifica pentru frecvente motive pentru care un fișier nu poate fi citit. Același lucru poate fi făcut pentru scrierea într-un fișier.

2021-11-28 22:59:55

Apreciem util și informativ răspuns. Am pus împreună într-un proiect pentru a vedea dacă pot să-l înțeleagă mai bine. Care este motivul pentru schimbarea readBytes metoda de semnătură de la ceea ce am avut? (a ta are un byte[] ca unul dintre argumente și returnează int). De asemenea, este ultimul bloc de cod destinate să facă parte din biblioteca sau la cerere?
Nerdy Bunz

de asemenea, nu linia "return (int) f.lungime();" accident de f.lungimea este mai mare decât Întreg.MAX_VALUE?
Nerdy Bunz

@NerdyBunz Despre ultima întrebare: nu, "downcasting" nu dă o eroare și, în acest caz, o IOException este aruncat atunci când fsize valoarea este prea mare. De asemenea, mi-ar fi re-utilizat fsize acolo (din f.length() rezultatele într-un operațiunea de I/O).
vanOekel

În legătură cu prima întrebare: totul este destinat să facă parte din biblioteca. Mea byte[] readFile(File f) este similar cu a ta byte[] readBytesFromFile(final File file). Mea byte[] readFileData(File f) metoda este un exemplu de modul în care puteți personaliza aceste funcții suplimentare. Am avut probleme cu imaginind care metode pentru a expune (public) și ține ascunse (private) și cred că este o întrebare la care numai tu poți răspunde: ce metode pe care doriți să utilizați aplicația, fără a fi restrictive la cerere?
vanOekel
3

Deși nu puteți folosi biblioteci terțe, puteți citi în continuare codul lor și să învețe din experiența lor. În Google Guava de exemplu, ai citit, de obicei, un fișier în bytes astfel:

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

Nucleul punerea în aplicare a acest lucru este toByteArrayInternal. Înainte de a suna acest lucru, ar trebui să verificați:

  • O not null fișier este trecut (NullPointerException)
  • Fișierul există (FileNotFoundException)

După aceea, este redus la o manipulare InputStream și aceasta în cazul în care IOExceptions vin de la. Când citiți fluxuri de o mulțime de lucruri de control al aplicației poate merge prost (rău sectoare și alte probleme de hardware, mal-funcționare drivere, sistem de OPERARE drepturi de acces) și se manifestă cu o IOException.

Sunt copierea aici punerea în aplicare:

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");
    }
  }

După cum puteți vedea cele mai multe dintre logica nu are de-a face cu citirea de fișiere în bucăți. Acest lucru este să se ocupe de situații, în cazul în care nu știți dimensiunea de InputStream, înainte de a începe lectura. In cazul tau, ai nevoie doar pentru a citi fișierele și ar trebui să fie capabil să știți lungimea în prealabil, astfel încât această complexitate ar putea fi evitate.

Alte verifica este OutOfMemoryException. În Java standard limita este prea mare, cu toate acestea în Android, acesta va fi mult mai mică valoare. Ar trebui să verificați, înainte de a încerca pentru a citi fișierul că nu există suficientă memorie disponibilă.

2021-11-26 13:42:23

În alte limbi

Această pagină este în alte limbi

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