Modify a .txt file in Java

I have a text file that I want to edit using Java. It has many thousands of lines. I basically want to iterate through the lines and change/edit/delete some text. This will need to happen quite often.

From the solutions I saw on other sites, the general approach seems to be:

  • Open the existing file using a BufferedReader
  • Read each line, make modifications to each line, and add it to a StringBuilder
  • Once all the text has been read and modified, write the contents of the StringBuilder to a new file
  • Replace the old file with the new file

This solution seems slightly "hacky" to me, especially if I have thousands of lines in my text file.

Anybody know of a better solution?


Solution 1:

I haven't done this in Java recently, but writing an entire file into memory seems like a bad idea.

The best idea that I can come up with is open a temporary file in writing mode at the same time, and for each line, read it, modify if necessary, then write into the temporary file. At the end, delete the original and rename the temporary file.

If you have modify permissions on the file system, you probably also have deleting and renaming permissions.

Solution 2:

if the file is just a few thousand lines you should be able to read the entire file in one read and convert that to a String.

You can use apache IOUtils which has method like the following.

public static String readFile(String filename) throws IOException {
    File file = new File(filename);
    int len = (int) file.length();
    byte[] bytes = new byte[len];
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(file);
        assert len == fis.read(bytes);
    } catch (IOException e) {
        close(fis);
        throw e;
    }
    return new String(bytes, "UTF-8");
}

public static void writeFile(String filename, String text) throws IOException {
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(filename);
        fos.write(text.getBytes("UTF-8"));
    } catch (IOException e) {
        close(fos);
        throw e;
    }
}

public static void close(Closeable closeable) {
    try {
        closeable.close();
    } catch(IOException ignored) {
    }
}

Solution 3:

You can use RandomAccessFile in Java to modify the file on one condition: The size of each line has to be fixed otherwise, when new string is written back, it might override the string in the next line.

Therefore, in my example, I set the line length as 100 and padding with space string when creating the file and writing back to the file.

So in order to allow update, you need to set the length of line a little larger than the longest length of the line in this file.

public class RandomAccessFileUtil {
public static final long RECORD_LENGTH = 100;
public static final String EMPTY_STRING = " ";
public static final String CRLF = "\n";

public static final String PATHNAME = "/home/mjiang/JM/mahtew.txt";

/**
 *  one two three
    Text to be appended with
    five six seven
    eight nine ten
 * 
 * 
 * @param args
 * @throws IOException
 */
public static void main(String[] args) throws IOException
{
    String starPrefix = "Text to be appended with";
    String replacedString = "new text has been appended";

    RandomAccessFile file = new RandomAccessFile(new File(PATHNAME), "rw");

    String line = "";
    while((line = file.readLine()) != null)
    {
        if(line.startsWith(starPrefix))
        {
            file.seek(file.getFilePointer() - RECORD_LENGTH - 1);
            file.writeBytes(replacedString);
        }

    }
}

public static void createFile() throws IOException
{
    RandomAccessFile file = new RandomAccessFile(new File(PATHNAME), "rw");

    String line1 = "one two three";
    String line2 = "Text to be appended with";
    String line3 = "five six seven";
    String line4 = "eight nine ten";

    file.writeBytes(paddingRight(line1));
    file.writeBytes(CRLF);
    file.writeBytes(paddingRight(line2));
    file.writeBytes(CRLF);
    file.writeBytes(paddingRight(line3));
    file.writeBytes(CRLF);
    file.writeBytes(paddingRight(line4));
    file.writeBytes(CRLF);

    file.close();

    System.out.println(String.format("File is created in [%s]", PATHNAME));
}

public static String paddingRight(String source)
{
    StringBuilder result = new StringBuilder(100);
    if(source != null)
    {
        result.append(source);
        for (int i = 0; i < RECORD_LENGTH - source.length(); i++)
        {
            result.append(EMPTY_STRING);
        }
    }

    return result.toString();
}

}

Solution 4:

If the file is large, you might want to use a FileStream for output, but that seems pretty much like it is the simplest process to do what you're asking (and without more specificity i.e. on what types of changes / edits / deletions you're trying to do, it's impossible to determine what more complicated way might work).

Solution 5:

No reason to buffer the entire file.

Simply write each line as your read it, insert lines when necessary, delete lines when necessary, replace lines when necessary.

Fundamentally, you will not get around having to recreate the file wholesale, especially if it's just a text file.