eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Regression testing is an important step in the release process, to ensure that new code doesn't break the existing functionality. As the codebase evolves, we want to run these tests frequently to help catch any issues early on.

The best way to ensure these tests run frequently on an automated basis is, of course, to include them in the CI/CD pipeline. This way, the regression tests will execute automatically whenever we commit code to the repository.

In this tutorial, we'll see how to create regression tests using Selenium, and then include them in our pipeline using GitHub Actions:, to be run on the LambdaTest cloud grid:

>> How to Run Selenium Regression Tests With GitHub Actions

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

When maintaining or transforming text data, we often need to modify the content of existing files. In Java, there are multiple ways to accomplish this, depending on the file size and our performance needs. Small files can be easily handled in memory, while large files are better processed line by line using streams.

In this tutorial, we’ll explore two approaches to modify a file’s content based on patterns. Both approaches rely on Java’s modern Java NIO APIs.

2. Introduction to the Problem

Before jumping into code, let’s define the problem we want to solve.

2.1. Defining the Problem

Imagine that we are given a text file containing lines about various programming languages. Here’s what it looks like:

Both JAVA and KOTLIN applications can run on the JVM.
But python is a simpler language
PYTHON application is also platform independent.
java and kotlin are statically typed languages.
On the other hand, python is a dynamically typed language.

We want to clean up and normalize this content by performing several text transformations:

  • Remove the second line.
  • Normalize capitalization – Convert all occurrences of “java”, “kotlin”, and “python” to their correctly capitalized forms “Java“, “Kotlin“, and “Python
  • Expand abbreviations – Wherever “JVM” appears, add its complete form “(Java Virtual Machine)” after it.
  • Write back the changes to the original file.

At the end, our modified file should look like this:

Both Java and Kotlin applications can run on the JVM (Java Virtual Machine).
Python application is also platform independent.
Java and Kotlin are statically typed languages.
On the other hand, Python is a dynamically typed language.

As usual, we’ll use unit test methods to demonstrate each approach. So next, let’s set up our unit test class.

2.2. Setting up the Test

Let’s first look at the unit test setup:

public class ModifyFileByPatternUnitTest {
    @TempDir
    private File fileDir;

    private File myFile;

    private static final List<String> ORIGINAL_LINES = List.of(
      "Both JAVA and KOTLIN applications can run on the JVM.",
      "But python is a simpler language",
      "PYTHON application is also platform independent.",
      "java and kotlin are statically typed languages.",
      "On the other hand, python is a dynamically typed language.");

    private static final List<String> EXPECTED_LINES = List.of(
      "Both Java and Kotlin applications can run on the JVM (Java Virtual Machine).",
      "Python application is also platform independent.",
      "Java and Kotlin are statically typed languages.",
      "On the other hand, Python is a dynamically typed language.");

    @BeforeEach
    void initFile() throws IOException {
        myFile = new File(fileDir, "myFile.txt");
        Files.write(myFile.toPath(), ORIGINAL_LINES);
    }

    // ...
}

As we can see, to ensure that our tests don’t modify any real files on the system, we’ll take advantage of the @TempDir annotation, which allows us to create a temporary directory that exists only for the duration of the test. Once the test finishes, JUnit automatically deletes all files inside it. This provides a clean, isolated environment for every run.

Additionally, the initFile() method ensures that every test begins with the same initial file content, thereby ensuring our modifications are tested in a controlled environment.

Next, let’s see how to solve this problem.

3. Loading the Entire File into Memory

Our first approach is to load the entire file into memory, modify its contents, and write it back. This technique is simple and effective for small or moderately sized files.

Let’s check how this is done:

@Test
void whenLoadTheFileContentToMemModifyThenWriteBack_thenCorrect() throws IOException {
    assertTrue(Files.exists(myFile.toPath()));
    List<String> lines = Files.readAllLines(myFile.toPath());
    lines.remove(1); // remove the 2nd line
    List<String> newLines = lines.stream()
      .map(line -> line.replaceAll("(?i)java", "Java")
        .replaceAll("(?i)kotlin", "Kotlin")
        .replaceAll("(?i)python", "Python")
        .replaceAll("JVM", "$0 (Java Virtual Machine)"))
      .toList();
    Files.write(myFile.toPath(), newLines);
    assertLinesMatch(EXPECTED_LINES, Files.readAllLines(myFile.toPath()));
}

In this approach, we first read all lines from the file into a List using Files.readAllLines(). Then, we removed the second line and performed the pattern replacements. Finally, we used Files.write() overwrites the file directly with the updated lines.

It’s worth mentioning that we used JUnit 5’s assertLinesMatch() to verify the file content.

This in-memory approach is concise and easy to reason about. However, it’s not ideal for huge files where memory usage becomes a concern.

So next, let’s assume myFile.txt is a huge file and explore how to solve the problem.

4. Memory-Efficient File Modification Using BufferedReader

To handle larger files efficiently, we can use streaming IO with BufferedReader and BufferedWriter. This method processes one line at a time without loading the entire file into memory.

Next, let’s have a look at the implementation:

@Test
void whenUsingBufferedReaderAndModifyViaTempFile_thenCorrect(@TempDir Path tempDir) throws IOException {
    Pattern javaPat = Pattern.compile("(?i)java");
    Pattern kotlinPat = Pattern.compile("(?i)kotlin");
    Pattern pythonPat = Pattern.compile("(?i)python");
    Pattern jvmPat = Pattern.compile("JVM");

    Path modifiedFile = tempDir.resolve("modified.txt");

    try (BufferedReader reader = Files.newBufferedReader(myFile.toPath());
      BufferedWriter writer = Files.newBufferedWriter(modifiedFile)) {

        int lineNumber = 0;
        String line;
        while ((line = reader.readLine()) != null) {
            lineNumber++;
            if (lineNumber == 2) {
                continue; // skip the 2nd line
            }

            String replaced = line;
            replaced = javaPat.matcher(replaced)
              .replaceAll("Java");
            replaced = kotlinPat.matcher(replaced)
              .replaceAll("Kotlin");
            replaced = pythonPat.matcher(replaced)
              .replaceAll("Python");
            replaced = jvmPat.matcher(replaced)
              .replaceAll("JVM (Java Virtual Machine)");

            writer.write(replaced);
            writer.newLine();
        }
    }

    Files.move(modifiedFile, myFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
    assertTrue(myFile.exists());
    assertLinesMatch(EXPECTED_LINES, Files.readAllLines(myFile.toPath()));
}

Next, let’s understand the details.

  • Precompiled Regex Patterns – Instead of calling replaceAll() directly on Strings, we compile the Regex patterns once using Pattern.compile(). This is more efficient when applying the same patterns multiple times.
  • Streaming with BufferedReader and BufferedWriterWe read the file line by line, modify each one, and write it immediately to a temporary output file (modified.txt). This keeps memory usage low regardless of file size.
  • Try-with-resources – The try (…) { … } syntax automatically closes both reader and writer when the block finishes. It’s a safer and cleaner alternative to manually closing streams in a finally block.
  • Replacing the original file – Once writing is complete, we replace the original file with the modified one using Files.move() and the REPLACE_EXISTING option.

This method is robust, scalable, and well-suited for large files or streaming scenarios that require fine-grained control over processing.

5. Conclusion

In this article, we explored two practical techniques for modifying a file’s content in Java based on regular expression patterns:

  • Loading the entire file into memory — It’s a simple and expressive solution, ideal for smaller files.
  • Streaming with BufferedReader and BufferedWriter — It’s an efficient and scalable approach for handling large files.

Both methods build upon Java’s standard libraries, so they work in any Java environment without extra dependencies. By combining these approaches, we can handle a wide range of file transformation tasks safely and efficiently.

As always, the complete source code for the examples is available over on GitHub.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)