views:

7710

answers:

10

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?

+5  A: 

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.

Andrei Krotkov
Turns out my answer was wrong; I forgot that RandomAccessFile doesn't allow insertions or deletions. I think this is the best answer.
Michael Myers
I ended up using this approach with a BufferedReader and BufferedWriter.
Zakir Hemraj
+1  A: 

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).

McWafflestix
A: 

In general you cannot edit the file in place; it's simply a very long sequence of characters, which happens to include newline characters. You could edit in place if your changes don't change the number of characters in each line.

lumpynose
A: 

I haven't written anything in Java in a long time, so take this with a grain of salt.

If the changes are confined to within lines only, open the input file for reading and open a temporary file for output. Write the lines to the temporary file as you go through the input file. When you are done with the input file, close both files and replace the input file with the output file.

Sinan Ünür
+1  A: 

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.

Will Hartung
A: 

What kind of data is it? Do you control the format of the file?

If the file contains name/value pairs (or similar), you could have some luck with Properties, or perhaps cobbling together something using a flat file JDBC driver.

Alternatively, have you considered not writing the data so often? Operating on an in-memory copy of your file should be relatively trivial. If there are no external resources which need real time updates of the file, then there is no need to go to disk every time you want to make a modification. You can run a scheduled task to write periodic updates to disk if you are worried about data backup.

James Van Huis
A: 

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) {
    }
}
Peter Lawrey
A: 

Can't you use regular expressions, if you know what you want to change ? Jakarta Regexp should probably do the trick.

Valentin Rocher
A: 

I think, FileOutputStream.getFileChannel() will help a lot, see FileChannel api http://java.sun.com/javase/6/docs/api/java/nio/channels/FileChannel.html

tamas rev