Lettura/Scrittura Byte in un File Utilizzando Solo Java.IO

0

Domanda

Come si può scrivere una matrice di byte in un file (e leggere nuovamente dal file) in Java?

Sì, sappiamo tutti che ci sono già un sacco di domande come questa, ma sono molto disordinato e soggettivo dovuto al fatto che ci sono tanti modi per eseguire questa operazione.

Quindi cerchiamo di ridurre la portata della questione:

Dominio:

  • Android / Java

Quello che vogliamo:

  • Veloce (per quanto possibile)
  • Bug-free (in rigidamente meticolosamente)

Cosa non si fa:

  • Librerie di terze parti
  • Tutte le librerie che richiedono API di Android oltre 23 (Marshmallow)

(Così, che esclude Apache Commons, Google Guava, Java.nio, e ci lascia con un buon ol' Java.io)

Ciò di cui abbiamo bisogno:

  • Matrice di Byte è sempre esattamente lo stesso (contenuto e dimensioni), dopo il passaggio attraverso la scrittura-poi-processo di lettura
  • Metodo di scrittura richiede solo due argomenti: il File e dati byte[]
  • Metodo di lettura restituisce un byte[] e richiede un solo argomento: il File

Nel mio caso particolare, questi metodi sono privati (non una libreria) e NON e ' responsabile per i seguenti, (ma se si desidera creare una soluzione universale che si applica a un pubblico più ampio, andare per esso):

  • Thread-safety (file non saranno accessibili da più di un processo alla volta)
  • File null
  • File indicando inesistente posizione
  • Mancanza di autorizzazioni alla posizione del file
  • Matrice di Byte di essere troppo grande
  • Matrice di Byte null
  • Trattare con "indice" di "lunghezza" o "aggiungi" argomenti/capacità

Quindi... siamo una sorta di ricerca della definitiva a prova di proiettile codice che la gente in futuro può assumere è sicuro da usare, perché la tua risposta ha un sacco di voti e non ci sono commenti che dicono, "Che potrebbe bloccarsi se..."

Questo è ciò che ho finora:

Scrivere I Byte Del File:

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

Leggere I Byte Dal File:

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

Da quello che ho letto, le possibili Eccezioni sono:

FileNotFoundException : ho capito bene che questo non dovrebbe accadere in quanto lungo il percorso del file forniti è stato ricavato utilizzando Android strumenti interni e/o se l'applicazione è stato testato correttamente?

IOException : io non so davvero che cosa potrebbe causare questo... ma io parto dal presupposto che non c'è modo intorno ad esso, se lo fa.

Quindi, con questo in mente... questi metodi possono essere migliorati o sostituiti, e se sì, con cosa?

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

Migliore risposta

6

Sembra che questi stanno per essere il nucleo di utilità/metodi di libreria che deve essere eseguito su Android API 23 o poi.

Relativi metodi di libreria, trovo che la cosa migliore da fare nessuna ipotesi sul modo in cui le applicazioni si utilizzano questi metodi. In alcuni casi le applicazioni desideri ricevere controllato IOExceptions (perché i dati da un file deve esistere per l'applicazione al lavoro), in altri casi, le applicazioni non possono care anche se i dati non sono disponibili (perché i dati da un file di cache solo che è disponibile anche da una fonte primaria).

Quando si tratta di operazioni di I/O, non c'è mai una garanzia che le operazioni di successo (ad es. un utente di far cadere il telefono in bagno). La biblioteca dovrebbe riflettere e dare l'applicazione di una scelta su come gestire gli errori.

Per ottimizzare le prestazioni di I/O sempre supporre che il "buon cammino" e individuare gli errori di capire cosa è andato storto. Questo è contro intuitivo alla normale programmazione, ma fondamentale per il rapporto con storage I/O. Per esempio, solo per controllare se un file esiste prima di leggere da un file può rendere la vostra applicazione due volte più lento di tutti questi tipi di I/O azioni di aggiungere velocemente per rallentare l'applicazione. Appena si supponga che il file esiste e se si ottiene un errore, solo allora controllare se il file esiste.

Quindi, dato che quelle idee, le funzioni principali come potrebbe apparire:

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 sull'implementazione:

  • I metodi possono anche gettare runtime-le eccezioni, come NullPointerExceptions - questi metodi non sono mai intenzione di essere "bug free".
  • Non credo che il buffer è necessario/voluto nei metodi di cui sopra in quanto solo un nativo di chiamata è fatto (vedi anche qui).
  • Ora l'applicazione ha anche la possibilità di leggere solo l'inizio di un file.

Per rendere più facile per un'applicazione per leggere un file, un metodo supplementare può essere aggiunto. Ma è da notare che la biblioteca di rilevare eventuali errori e li la domanda, in quanto l'applicazione non è più possibile rilevare tali errori.

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

Questa implementazione aggiunge un altro punto nascosto del fallimento: OutOfMemory al punto in cui i nuovi dati viene creata la matrice.

Per soddisfare i requisiti di applicazioni supplementari metodi possono essere aggiunti per aiutare con diversi scenari. Per esempio, diciamo che in applicazione davvero non si vuole affrontare con eccezioni checked:

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

Il metodo fileExceptionToRuntime è un'implementazione minima, ma mostra l'idea qui.

La libreria può anche aiutare un'applicazione per risolvere i problemi quando si verifica un errore. Per esempio, un metodo canReadFile(File f) potrebbe controllare se un file esiste ed è leggibile e non troppo grande. L'applicazione potrebbe chiamare una funzione di file-non riesce a leggere e verificare i motivi per cui un file non può essere letto. Lo stesso può essere fatto per la scrittura di un file.

2021-11-28 22:59:55

Apprezzare la gentile risposta. Sto mettendo insieme in un progetto per vedere se riesco a capire meglio. Qual è il motivo per cambiare la readBytes firma del metodo da quello che ho avuto? (il vostro prende un byte[] come uno degli argomenti e restituisce int). Anche il tuo ultimo blocco di codice che intende essere parte della biblioteca o l'applicazione?
Nerdy Bunz

inoltre, non "return (int) f.length();" crash poiché la f.la lunghezza è maggiore di numero Intero.MAX_VALUE?
Nerdy Bunz

@NerdyBunz Circa l'ultima domanda: no, "downcast" non dà un errore e in questo caso, viene generata un'eccezione IOException quando il fsize il valore è troppo grande. Inoltre, mi hanno ri-usato fsize c' (dal f.length() risultati in un'operazione di I/O).
vanOekel

Circa la prima domanda è: tutto questo è destinato a far parte della biblioteca. Il mio byte[] readFile(File f) è simile al tuo byte[] readBytesFromFile(final File file). Il mio byte[] readFileData(File f) il metodo è un esempio di come è possibile personalizzare queste funzioni ulteriori. Ho avuto difficoltà a capire quali sono i metodi per esporre (public) e tenere nascosto (private) e penso che è una domanda solo tu puoi rispondere: quali metodi si desidera che l'applicazione per utilizzare senza essere restrittivi per l'applicazione?
vanOekel
3

Anche se non è possibile utilizzare le librerie di terze parti, è ancora possibile leggere il loro codice e imparare dalla loro esperienza. In Google Guava per esempio, è di solito di leggere un file in byte, come questo:

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

L'implementazione di base di questo è toByteArrayInternal. Prima di chiamare questo, si dovrebbe controllare:

  • Not null file è passato (NullPointerException)
  • Il file esiste (FileNotFoundException)

Dopo di che, si è ridotto per la gestione di un InputStream e questo dove IOExceptions provengono da. Durante la lettura dei flussi di di un sacco di cose al di fuori del controllo dell'applicazione possono andare male (settori danneggiati e altri problemi hardware, mal-funzionamento driver, OS diritti di accesso) e si manifestano con una IOException.

Io sono la copia di qui l'applicazione:

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

Come si può vedere la maggior parte della logica ha a che fare con la lettura di un file in blocchi. Questo è quello di gestire le situazioni, in cui non si conosce la dimensione del InputStream, prima di iniziare la lettura. Nel tuo caso, è solo bisogno di leggere i file e si dovrebbe essere in grado di conoscere la lunghezza di anticipo, in modo tale complessità può essere evitata.

L'altro controllo è OutOfMemoryException. In Java standard il limite è troppo grande, comunque in Android, sarà un valore molto più piccolo. Si dovrebbe verificare, prima di provare a leggere il file che c'è abbastanza memoria disponibile.

2021-11-26 13:42:23

In altre lingue

Questa pagina è in altre lingue

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