web-dev-qa-db-ja.com

Stream- Java 8を使用してファイル内のテキストを置き換える

特定の部分文字列を含むすべての行をテキストファイル内の別の文字列に置き換えるAPIを作成しようとしています。

Java 8ストリームを使用して、指定されたパターンを含む行をフィルタリングしています。ファイルの書き込み部分に問題があります。

Files.lines(targetFile).filter(line -> line.contains(plainTextPattern)).parallel()
      .map(line-> line.replaceAll(plainTextPattern, replaceWith)).parallel();

上記のコードは、ファイルを行ごとに読み取り、パターンに一致する行をフィルタリングして、give文字列に置き換え、置き換えられた行のみを含む文字列のストリームを返します。

これらの行をファイルに書き戻す必要があります。パイプラインが終了するとストリームが失われるため、パイプラインに次を追加しました。

.forEach(line -> {
try {
            Files.write(targetFile, line.toString().getBytes());
} catch (IOException e) {
  e.printStackTrace();
}

変更された(パイプラインにあるため)行のみをファイルに書き込み、他の行は変更されないようにすることを望んでいました。

ただし、ファイル内の各行のファイルが切り捨てられ、最後に処理された行のみが保持され、パイプラインで一致しなかったすべての行が削除されるようです。

ストリームを使用したファイルの処理について何か足りないものはありますか?

7
A.R.K.S

filterを使用すると、フィルターに一致しないものがストリームから削除されます。 (さらに、その価値については、a)parallelを1回だけ使用する必要があります。b)parallelは、I/Oソースからのストリームではそれほど効果的ではありません。c)ほとんどありません。実際に非並列を試し、遅すぎることがわかるまで、parallelを使用することをお勧めします。)

つまり、replaceAllを実行する場合は、パターンに一致する行を除外する必要はありません。コードは次のようになります。

try (Stream<String> lines = Files.lines(targetFile)) {
   List<String> replaced = lines
       .map(line-> line.replaceAll(plainTextPattern, replaceWith))
       .collect(Collectors.toList());
   Files.write(targetFile, replaced);
}
21
Louis Wasserman

申し訳ありませんが、これはファイルの動作方法ではありません。ファイルの途中に書き込みたい場合は、RandomAccess;が必要です。 FilePointerを取得し、そのポインターをシークし、そこから書き込みます。

これは、書き込みたいデータのサイズが上書きしたいデータのサイズと等しい場合に当てはまります。そうでない場合は、ファイルの末尾を一時バッファにコピーして、書き込みたいテキストに追加する必要があります。

そしてところで、parallelStreamsバインドされたタスクのIOはしばしば悪い考えです。

7
Sleiman Jneidi