Replace text in a file using Stream- Java 8

18,253

Solution 1

Using filter eliminates anything that doesn't match the filter from the stream. (Additionally, for what it's worth, a) you only need to use parallel once, b) parallel isn't that effective on streams coming from I/O sources, c) it's almost never a good idea to use parallel until you've actually tried it non-parallel and found it too slow.)

That said: there's no need to filter out the lines that match the pattern if you're going to do a replaceAll. Your code should look like this:

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

Solution 2

So sorry to tell you that this is not how files work. If you want to write to the middle of a file, you need to have RandomAccess; Get a FilePointer, seek, that pointer, and write from there.

This holds if the size of data you want write is equal to the size of data you want to overwrite. If this is not the case, you have to copy the tail of the file to a temp buffer and append it to the text you wish to write.

And btw, parallelStreams on IO bound tasks is often a bad idea.

Share:
18,253
A.R.K.S
Author by

A.R.K.S

Updated on July 27, 2022

Comments

  • A.R.K.S
    A.R.K.S almost 2 years

    I'm trying to write an API to replace all the lines containing a certain substring with a different string in a text file.

    I’m using Java 8 stream to filter the lines which contains the given pattern. I’m having problem with the file write part.

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

    Above code reads the file line-wise, filters the lines that match the pattern and replaces with the give string and gives back a stream of strings which has only the replaced lines.

    We need to write these lines back to file. Since we lose the stream once the pipeline ends, I appended the following to the pipeline:

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

    I was hoping it would write to the file only the modified (since it is in the pipeline) line and keep the other lines untouched.

    But it seems to truncate the file for each line in the file and keep only the last processed line and deletes all the lines that were not matched in the pipeline.

    Is there something I’m missing about handling files using streams?

  • A.R.K.S
    A.R.K.S over 8 years
    All the comments were very informative! Thank you all!