43

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

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

プロジェクトでPMDを実行したところ、回線で「 AssignmentInOperand」違反が発生しましたwhile (...)

明白な以外に、このループを実行する簡単な方法はありますか?

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

これはより良い習慣と考えられていますか?(コードを「複製」しますがline = br.readLine()?)

4

8 に答える 8

38

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

于 2012-08-22T21:56:18.920 に答える
24

java-8でのストリーム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
}
于 2014-04-04T08:08:28.047 に答える
22

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

(C#には、より優れたオプションがあります。foreachで反復できるを返すメソッドIEnumerable<string>です。これは、拡張されたforループの最後に自動破棄がないため、Javaではそれほど優れていません...また、IOExceptionイテレータからスローすることはできません。つまり、一方をもう一方のドロップイン置換にすることはできません。)

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

于 2011-01-13T06:30:58.587 に答える
21

私は日常的に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()呼び出しコードを複製していますが、ロジックは明確です。

私が構造を使用するもう1つのとき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ループの代替案が本当に好きかどうかはわかりませんが、PMDツールを満足させることができ、ロジックはまだ明確です。

于 2014-03-12T12:25:40.743 に答える
4

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
}
于 2011-01-13T06:41:55.817 に答える
3

GoogleのGuavaライブラリは、静的メソッド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();
    }
}
于 2013-07-15T13:30:29.790 に答える
3

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

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

Java 8以前は、その明快さと繰り返しを必要としないため、私のお気に入りでした。IMObreakは、副作用のある式に適したオプションです。しかし、それはまだイディオムの問題です。

于 2015-01-04T06:54:26.590 に答える
1

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

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

于 2011-01-13T06:37:54.357 に答える