web-dev-qa-db-ja.com

テキストファイルの内容を1行ずつ繰り返します-ベストプラクティスはありますか? (対PMDのAssignmentInOperand)

Javaテキストファイルを読むことを知っているいくつかのモジュールを備えたアプリケーションがあります。彼らはこのようなコードで非常に簡単にそれを行います:

_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()コードを「複製」しますか?)

37
RonK

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

(C#には、より良いオプションがあります。foreachで反復処理できるIEnumerable<string>を返すメソッドです。Javaでは自動破棄がないため、Niceではありません。拡張されたforループの終わり...また、イテレータからIOExceptionをスローできないため、一方を他方のドロップイン置換にすることはできません。

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

20
Jon Skeet

私は古い投稿であることを知っていますが、私は同じニーズを(ほぼ)持っていたので、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

33

Java-8の streams および Lambdas および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
}
20
Aksel Willgert

私は定期的にwhile((line = br.readLine()) != null)コンストラクトを使用しています...しかし、 最近このニースの代替案に出くわしました

_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[]_配列に読み込むときです...

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

これはforループに変換することもできます:

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

For-loopの選択肢を本当に好むかどうかはわかりませんが…、PMDツールを満足させ、ロジックはまだ明確です。

15
rolfl

Jonの答えに基づいて、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();
            }

        };
    }

}

公正な警告:これにより、読み取り中に発生する可能性のあるIOExceptionが抑制され、読み取りプロセスが単純に停止します。イテレータメソッドのセマンティクスが明確に定義されており、for-each構文を使用するために準拠する必要があるため、ランタイム例外をスローせずにJavaでこれを回避する方法があるかどうかは不明です。ここで複数のイテレータを実行すると、奇妙な動作が発生するため、これが推奨されるかどうかはわかりません。

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

とにかく、これを一種のデコレーターとして使用してfor-each構文の利点を得ることができます。

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

次の選択肢が言及されていないことに少し驚いています。

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

Java 8の前は、その明快さと繰り返しを必要としないため、私のお気に入りでした。IMO、breakは、副作用のある式に適したオプションです。しかし。

3
Mario Rossi

Googleの Guava Libraries は、静的メソッド CharStreams.readLines(Readable、LineProcessor <T>) を使用して、各行を処理するための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();
    }
}
3
Mathias

AssignmentInOperandはPMDで物議を醸すルールです。このルールの理由は、「これによりコードが複雑になり読みにくくなる可能性がある」です[ http://pmd.sourceforge.net/rules/controversialを参照してください。 html

本当にそのようにしたい場合は、そのルールを無効にすることができます。私の側では、前者を好みます。

1
longbkit