Come gestire IOException quando il file da aprire è già stato verificato?

0

Questa è una domanda specifica qui, ma mi interessa la "best practice" generale per situazioni simili in quanto sono nuovo di Java.

Supponiamo di avere un codice Java che deve aprire un file (vedi sotto per il codice). Per prima cosa ho una funzione che controlla la presenza dei file. Se il file esiste, chiamiamo funzioni per aprirlo ed elaborarlo. Altrimenti restituiamo un messaggio all'utente che dichiara che il file non è stato trovato.

Ora nelle funzioni che aprono il file, dobbiamo ancora avere un'istruzione try / catch per la possibile IOException perché è un'eccezione controllata. La funzione openSpecifiedFile deve restituire un FileInputStream. Il fatto che il nostro file sia stato dimostrato di esistere diversi millisecondi fa non è sufficiente per garantire che l'istruzione catch non venga mai eseguita (anche se è improbabile) quindi preferirei non restituire null qui.

C'è una via per restituire un oggetto predefinito, o semplicemente evitare l'istruzione di ritorno nulla tutti insieme e uscire dal programma con qualche tipo di eccezione di runtime? L'unico modo in cui le cose potrebbero andare male qui è che se qualcosa di molto brutto fosse successo, mi sento ...

Suppongo che la domanda generale sia "Quando si eseguono i controlli per assicurarsi che non si verifichino determinate eccezioni controllate, qual è un buon modo per gestire i blocchi try / catch necessari?"

 public static void main(String[] args) {
    String filename = args[0];
    if (specifiedFileExists(filename)) {
        FileInputStream specifiedFile = openSpecifiedFile(filename);
        processFile(specifiedFile);
    } else
        System.out.println("The specified file does not exist");
}


private static boolean specifiedFileExists(String filename) {
    File currentFile = new File(filename);
    return currentFile.exists();
}

private static FileInputStream openSpecifiedFile(String filename) {
    try {
        return new FileInputStream(filename);
    } catch (IOException e) {}
    return null;
}

private static void processFile(FileInputStream currentFile) {
    ByteBuffer filledBuffer = fillBufferFromFile(currentFile);
    String messageFromFile = processBufferToString(filledBuffer);
    System.out.println(messageFromFile);
}

private static ByteBuffer fillBufferFromFile(FileInputStream currentFile) {
    try {
        FileChannel currentChannel = currentFile.getChannel();
        ByteBuffer textBuffer = ByteBuffer.allocate(1024);
        currentChannel.read(textBuffer);
        textBuffer.flip();
        return textBuffer;
    } catch (IOException e) {}
    return ByteBuffer.allocate(0);
}

private static String processBufferToString(ByteBuffer filledBuffer) {
    StringBuilder characterBuilderFromFile = new StringBuilder();
    while (filledBuffer.hasRemaining())
        characterBuilderFromFile.append((char) filledBuffer.get());
    return characterBuilderFromFile.toString();
}
    
posta Johnathan 14.01.2017 - 11:59
fonte

4 risposte

0

Mentre il suggerimento di @ gnasher729 è probabilmente la migliore risposta in questa specifica situazione , qui c'è una domanda più generale, che è (parafrasi): "come gestisco le eccezioni controllate che non dovrebbero mai accadere?" E nei casi in cui non è possibile utilizzarli per eliminare un controllo ridondante (e potenzialmente errato), l'alternativa migliore è probabilmente ripensarli come un'eccezione non controllata. Nella maggior parte dei miei progetti, di solito finisco con un'eccezione simile a questa:

 public class UnexpectedException extends RuntimeException
 {
       public UnexpectedException (Throwable cause, String whyItShouldntHaveHappened)
       { 
           super ("Caught an exception with description: " + cause.toString() + 
                  "\nThis shouldn't have happened because: " + whyItShouldntHaveHappened, 
                  cause); 
       }
 }

UnexpectedException viene quindi gestito al livello più alto dell'applicazione e viene registrato ovunque si possa pensare di registrarlo. Anche se la registrazione è disattivata. Perché ogni occasione in cui uno viene effettivamente lanciato è la prova diretta di un bug. Non ha costruttori diversi da quello mostrato, perché hai sempre bisogno di entrambi i parametri.

    
risposta data 15.01.2017 - 20:08
fonte
7

Controllare l'esistenza di un file e quindi aprirlo è sempre aperto alle condizioni di gara. Dall'apertura del file verrà rilevato se il file non esiste e lo gestirà, il controllo dell'esistenza del file è in realtà inutile. Basta aprire il file e gestire le eccezioni.

Direi anche che inizi con un nome di file e da quel nome di file vuoi ottenere una stringa. Quindi scrivi una funzione che accetta un nome file e restituisce una stringa o genera un'eccezione. Quindi una funzione che restituisce una stringa, dato un nome file, e una funzione che elabora una stringa.

    
risposta data 14.01.2017 - 13:19
fonte
2

Il problema principale che devi affrontare è che non sai davvero cosa dovrebbe fare il programma quando il file non può essere aperto all'interno del metodo openSpecifiedFile.

In una situazione come questa è forse meglio catturare l'eccezione e poi ricrearla. Se ha senso aggiungere ulteriori informazioni all'eccezione, fallo pure.

Il ritorno di un oggetto predefinito potrebbe sorprendere molto bene voi o gli utenti delle vostre funzioni in futuro.

Quando non è possibile aprire un file che esisteva molto poco prima non è una buona pratica fingere che non sia successo niente di male.

    
risposta data 14.01.2017 - 12:47
fonte
1

Qui ci sono due problemi:

  1. I / O è soggetto a errori per una serie di motivi, ed è per questo motivo che una gestione delle eccezioni appropriata è importante. Non c'è modo di verificare che un file esista e garantire che continuerà ad esistere anche una frazione di secondo dopo. Altri processi, compresi quelli sepolti nel sistema operativo, possono violare tale assunto.

  2. Ciò che proponi è goffo e va contro ciò che la stragrande maggioranza degli altri programmatori assumerebbe come dovrebbe essere progettato quel codice.

Per fortuna, c'è una soluzione semplice.

Dai un'occhiata a JavaDoc per IOException . Nella parte superiore della pagina c'è un elenco di sottoclassi. Puoi prenderli, e in effetti dovresti catturarli per sottotipo se tieni che il programma si comporta in modo diverso in base alla condizione di errore.

try {
  FileInputStream in = getStream();
}
catch (FileNotFoundException e) {
  // ...
}
catch (IOException e) {
  // ...
}

Ecco come gli sviluppatori si aspettano che il codice sia strutturato, perché è chiaro e funziona bene . In quei blocchi di catch puoi avvolgere IOException in RuntimeException o anche in Error . Puoi restituire null o fare qualsiasi altra cosa tu voglia fare. Ma la gestione degli errori è coerente e si trova insieme.

    
risposta data 15.01.2017 - 20:30
fonte

Leggi altre domande sui tag