2011-01-13 2 views
32

私たちは、テキストファイルを読むことを知っている少数のモジュールを持つJavaアプリケーションを持っています。彼らは、このようなコードを非常に単純にそれを行う:テキストファイルの内容を1行ずつ反復する - ベストプラクティスはありますか? (対PMDのAssignmentInOperand)

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

私は私のプロジェクトにPMDを走り、while (...)ラインの「AssignmentInOperand」違反を得ました。

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

が、これは、より良い練習を考えられている:

明らか以外にこのループを行うための簡単な方法はありますか? (私たちはline = br.readLine()コードを「複製」していますが)

+0

いいえBufferedReaderIterator。 私はr.mark(1)をr.mark(2)に置き換えなければなりませんでした。さもなければ大きなファイルに約100行の "無効マーク"があります。理由を理解しないでください。 –

+4

'for'ループはどうですか? –

答えて

19

私は一般的に前者を好みます。私はを一般的にのような比較の副作用が好きではありませんが、この特定の例は非常に一般的で便利なイディオムであり、私はそれに反対しません。

(C#では、foreachを使用して反復処理できるIEnumerable<string>を返すメソッドがありますが、これは拡張forループの最後に自動ディスパッチがないため、Javaではうまくありません。また、イテレータからIOExceptionを投げることができないので、もう1つのドロップインを他のものに置き換えることはできません)。

別の言い方をすると、重複するラインの問題により、オペランド内代入問題よりも優先されます。私はこのパターンを一目で理解することに慣れています - 重複した行バージョンで、すべてを正しい場所に停止してチェックする必要があります。それはおそらく他のものと同じくらい習慣ですが、私はそれが問題だとは思わない。

+0

私は、デコレータを作成する便利な仕組みとして、デコレータを作成することについてどう思っているのですか?あなたがforeachループを使うことができるように、反復のセマンティクスを記述してください。(私のレスポンスは以下の* rough * suggestionを参照してください)... –

+0

@マークE:それはC#バージョンと同じくらい綺麗ではありません。私はあなたの答えにコメントし、私の編集します。 –

2

AssignmentInOperand PMDで物議ルールで、このルールの理由がある:「これは、コードが複雑で読みにくくすることができます」(http://pmd.sourceforge.net/rules/controversial.htmlをご参照ください)

あなたが本当にしたい場合は、そのルールを無効にすることができそのようにしてください。私の側で私は前者を好む。私はあなたが、foreachループを使用できるようにファイルイテレータとして機能するデコレータを作成するのに十分な簡単なはず考えることになったジョンの答えに基づいて

+1

また、 '/ NOPMD'のコメントを書いてください –

4

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

     }; 
    } 

} 

フェア警告:これはそれかもしれないIOExceptionsを抑制読み取り中に発生し、読み取りプロセスを単に停止します。実行時の例外を投げずに、Javaでこれを回避する方法があることは明らかです。for each構文を使用するには、反復子メソッドのセマンティクスが適切に定義されている必要があります。また、ここで複数のイテレータを実行すると、いくつかの奇妙な動作が発生します。私はこれが推奨されているかどうかはわかりません。

私はこれをテストしましたが、動作します。

とにかく、あなたはの利益を得るために、各デコレータの種類としてこれを使用して構文:私は知っている

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

' readLine'がIOExceptionを投げる可能性があるので、これはコンパイルされないと思われます。 Iteratorインターフェースはそれを許さないので、チェックされていない例外でそれをラップする必要があります。その時点で、元のコードのように見えなくなり始めます。( –

+0

@Jon:あなたは正しいですが、残念ながら私はセマンティクスを得るために例外を隠す方法はないと確信しています。利便性は良いが、利払いは暗く思われる。 –

29

が古いポストですが、私はちょうど同じ必要性を持っていた(ほぼ)、私はそれを解決Apache CommonsのFileUtilsのLineIteratorを使用しています。自分のjavadocから :

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

は、ドキュメントを参照してください: http://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/LineIterator.html

+0

ありがとう、それは将来便利になるだろう。 – RonK

+1

誰もがこの依存関係を必要とする場合にはMavenのから(のfileutils): コモンズ-IO コモンズ-IO 2.4

+0

私はあなたが 'スキャナとの事のこの同じ種類を行うことができますかなり確信しています「あまりにも。 –

3

GoogleのGuava Librariesは、各ラインを処理するためのLineProcessor<T>の実装を静的メソッドCharStreams.readLines(Readable, LineProcessor<T>)を使用して、代替ソリューションを提供します。

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

whileループの本体は、現在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(); 
    } 
} 
13

私は日常while((line = br.readLine()) != null)構文を使用...しかし、recently I came accross this nice alternative

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

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

など、これはまだreadLine()コールコードを複製されますが、ロジックが明確です私がwhile((...) ...)構成を使用しているのは、ストリームからbyte[]配列を読み込むときです。

私は本当にforループ選択肢を好むことを確認していない

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

....しかし、それはすべてのPMDツールを満足させる、と:

これもしてforループの中で変換することができますロジックはまだクリアされています。

+1

良いアプローチ!Java 7で使用されている場合、' BufferedReader'インスタンスの作成をtry-with-resourcesステートメントでラップすることもできます。 –

11

java-8のstreamsLambdasとjava-7のTry-With-Resourcesのサポートにより、よりコンパクトな構文で必要なものを実現できます。

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 
} 
+1

ラムダをメソッド参照に短くすることもできます: 'lines.forEachOrdered(System.out :: println)' –

3

私は少しは以下の選択肢が言及されていなかった驚い:

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

のJava 8の前に、それは、その明快さの私のお気に入りだったと繰り返しを必要としません。 IMO、breakは、副作用を伴う式よりも優れたオプションです。それでも、イディオムの問題です。

関連する問題