Scorrere il contenuto di una riga file di testo per riga - c'è una buona pratica? (Vs AssignmentInOperand di PMD)

StackOverflow https://stackoverflow.com/questions/4677411

  •  10-10-2019
  •  | 
  •  

Domanda

Abbiamo un'applicazione Java che ha un paio di moduli che sanno leggere i file di testo. Lo fanno semplicemente con un codice come questo:

BufferedReader br = new BufferedReader(new FileReader(file));  
String line = null;  
while ((line = br.readLine()) != null)  
{  
   ... // do stuff to file here  
} 

I corse PMD sul mio progetto ed ha ottenuto la ' AssignmentInOperand ' violazione sulla linea while (...).

C'è un modo più semplice di fare questo circuito diverso da quello ovvio:

String line = br.readLine();  
while (line != null)  
{  
   ... // do stuff to file here  
   line = br.readLine();  
} 

E 'questo considerato una pratica migliore? (Anche se abbiamo "duplicato" il codice line = br.readLine()?)

È stato utile?

Soluzione

In generale, preferisco la prima. Io non in generale come effetti collaterali all'interno di un confronto, ma questo particolare esempio è un idioma che è così comune e così a portata di mano che non lo faccio oggetto ad esso.

(In C # c'è un'opzione più bello: un metodo per restituire un IEnumerable<string> cui è possibile iterare con foreach, che non è così bello in Java perché non c'è auto-smaltire alla fine di una maggiore ciclo for ... e anche perché non si può buttare IOException dal iteratore, che significa che è possibile non solo fare un un rimpiazzo per l'altro.)

Per dirla in altro modo: i duplicati fastidi problema linea di me più di quanto l'assegnazione-entro-operando problema. Sono abituato a prendere in questo schema a colpo d'occhio - con la versione a linea duplicata ho bisogno di fermare e controllare che tutto è al posto giusto. Questo è probabilmente un'abitudine tanto quanto qualsiasi altra cosa, ma non credo che sia un problema.

Altri suggerimenti

Lo so è un vecchio post, ma ho appena avuto la stessa necessità (quasi) e mi risolverlo utilizzando un LineIterator da fileutils in Apache Commons. Dal loro javadoc:

LineIterator it = FileUtils.lineIterator(file, "UTF-8");
try {
    while (it.hasNext()) {
    String line = it.nextLine();
    // do something with line
    }
} finally {
    it.close();
}

Controllare la documentazione: http: // Commons .apache.org / corretto / commons-IO / javadocs / api-release / org / apache / comuni / IO / LineIterator.html

Il supporto per flussi e Lambda in java-8 e Prova-Con-Resources di java-7 consente di achive quello che vuoi in più compatto sintassi.

Path path = Paths.get("c:/users/aksel/aksel.txt");

try (Stream<String>  lines = Files.lines(path)) {
    lines.forEachOrdered(line->System.out.println(line));
} catch (IOException e) {
    //error happened
}

Io abitualmente uso il costrutto while((line = br.readLine()) != null) ... ma, recente ho mai incontrato questo bel alternativa:

BufferedReader br = new BufferedReader(new FileReader(file));

for (String line = br.readLine(); line != null; line = br.readLine()) {
   ... // do stuff to file here  
}

Questa è ancora duplicare il codice di chiamata readLine(), ma la logica è chiara, ecc.

L'altra volta che uso il costrutto while(( ... ) ...) è durante la lettura da un flusso in una matrice byte[] ...

byte[] buffer = new byte[size];
InputStream is = .....;
int len = 0;
while ((len = is.read(buffer)) >= 0) {
    ....
}

Questo può anche essere trasformato in un ciclo for con:

byte[] buffer = new byte[size];
InputStream is = .....;
for (int len = is.read(buffer); len >= 0; len = is.read(buffer)) {
    ....
}

Non sono sicuro ho davvero preferisco le alternative per-loop .... ma, è in grado di soddisfare qualsiasi strumento PMD, e la logica è ancora chiaro, ecc.

In base a risposta di Jon ho avuto modo di pensare dovrebbe essere abbastanza facile creare un decoratore di agire come un file iteratore in modo da poter utilizzare un ciclo foreach:

public class BufferedReaderIterator implements Iterable<String> {

    private BufferedReader r;

    public BufferedReaderIterator(BufferedReader r) {
        this.r = r;
    }

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {

            @Override
            public boolean hasNext() {
                try {
                    r.mark(1);
                    if (r.read() < 0) {
                        return false;
                    }
                    r.reset();
                    return true;
                } catch (IOException e) {
                    return false;
                }
            }

            @Override
            public String next() {
                try {
                    return r.readLine();
                } catch (IOException e) {
                    return null;
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

}

Fair Warning: Ciò sopprime IOExceptions che potrebbero verificarsi durante legge e semplicemente si ferma il processo di lettura. Non è chiaro che c'è un modo per aggirare questo in Java senza gettare eccezioni di runtime come la semantica dei metodi iteratore sono ben definiti e devono essere conformi al fine di utilizzare il for-each sintassi. Inoltre, l'esecuzione di più iteratori qui avrebbe qualche strano comportamento; quindi non sono sicuro che questo è raccomandato.

Ho fatto prova di questo, però, e funziona.

In ogni caso, si ottiene il vantaggio di for-each sintassi di utilizzare questo come una sorta di decoratrice:

for(String line : new BufferedReaderIterator(br)){
    // do some work
}

Google Guava Biblioteche fornire una soluzione alternativa utilizzando il metodo statico < a href = "http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/io/CharStreams.html#readLines%28java.lang.Readable,%20com.google .common.io.LineProcessor% 29" rel = "nofollow"> CharStreams.readLines (leggibili, LineProcessor ) con un'implementazione di LineProcessor<T> per elaborare ogni riga.

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    CharStreams.readLines(br, new MyLineProcessorImpl());
} catch (IOException e) {
    // handling io error ...
}

Il corpo del ciclo while si trova ora nella realizzazione LineProcessor<T>.

class MyLineProcessorImpl implements LineProcessor<Object> {

    @Override
    public boolean processLine(String line) throws IOException {
        if (// check if processing should continue) {
            // do sth. with line
            return true;
        } else {
            // stop processing
            return false;
        }
    }

    @Override
    public Object getResult() {
        // return a result based on processed lines if needed
        return new Object();
    }
}

Sono sorpreso la seguente alternativa non era menzionato un po ':

while( true ) {
    String line = br.readLine();
    if ( line == null ) break;
    ... // do stuff to file here
}

Prima di Java 8 è stato il mio preferito per la sua chiarezza e che non richiedono la ripetizione. IMO, break è una scelta migliore per le espressioni con effetti collaterali. E 'ancora una questione di idiomi, però.

AssignmentInOperand è una regola controverso PMD, la ragione di questa regola è: "questo può rendere il codice più complicato e difficile da leggere" (si veda http://pmd.sourceforge.net/rules/controversial.html )

Si potrebbe disabilitare questa regola se si vuole veramente farlo in quel modo. Nella parte mia preferisco il primo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top