-1

I have a csv file thats formatted like this id,text. Here is an example:

helloText,How are you
goodbyeMessage,Some new text for a change
errorMessage,Oops something went wrong

Now lets say for example I want to edit the text part of goodbyeMessage which is Some new text for change, to See you later

The resulting csv should then look like this:

helloText,How are you
goodbyeMessage,See you later
errorMessage,Oops something went wrong

I have code that can write to the file but when the code finishes executing, this is the resulting csv file:

helloText,How are you
goodbyeMessage,Some new text for a change
errorMessage,Oops something went wronggoodbyeMessage,See you later

I know this is occurring because I set the FileWriter's append value to true. If I don't everything gets wiped.

I have tried using FileWriter.newLine() to make it look better but that is not what I am trying to achieve. I still want the same number of line in the file. MyApp.java

public static void main(String[] args) throws FileNotFoundException, IOException {
    PropsWriter pw = new PropsWriter("test_props.txt");
    pw.updateElementText("goodbyeMessage", "See you later");
}

PropsWriter.java

/**
 * Updates the text of a given element in the properties file.
 * 
 * @param id The id of the element
 * @param newText The text that will replace the original text.
 * 
 * @throws IOException If I/O error occurs
 */
public void updateElementText(String id, String newText) throws IOException {
    Assertions.checkNotNull(id, "Id must not be null.");
    Assertions.checkNotNull(id, "Id must not be an empty string.");
    File file = new File(pathName);
    BufferedReader br = new BufferedReader(new FileReader(file));
    BufferedWriter wr = new BufferedWriter(new FileWriter(file, true));
    try {
        String line;

        while((line = br.readLine()) != null) {
            if(line.contains(id)) {
                //returns true
                System.out.println("Is line there: " + line.contains(id));

                //returns helloText
                System.out.println("ID: " + extractId(line));

                //returns How are you
                System.out.println("TEXT: " + extractText(line));

                //returns Some new text for a change
                System.out.println("NEW_TEXT: " + newText);

                // This is where I am trying to replace the old text 
                // with new text that came from the main method.
                line = line.replaceAll(extractText(line), newText); 
                //wr.newLine();
                wr.write(line);             
            }
        }
    } catch(IOException e) {
        e.printStackTrace();
    } finally {
        wr.close();
    }
}

/**
 * Gets the id part of a line that is stored in the 
 * properties file.
 * 
 * @param element The element the id is got from.
 * @return String representation of the id.
 */
private static String extractId(String line) {
    final int commaOccurence = getFirstCommaOccurrence(line);
    return line.substring(0, commaOccurence);
}

/**
 * Gets the text part of a line that is stored in the 
 * properties file.
 * 
 * @param element The element the text is got from.
 * @return String representation of the text.
 */
private static String extractText(String line) {
    final int commaOccurence = getFirstCommaOccurrence(line);
    return line.substring(commaOccurence + 1, line.length());
}

/**
 * Gets the first occurrence of a comma in any given line of a text file.
 * @param element
 * @return
 */
private static int getFirstCommaOccurrence(String line) {
    return line.indexOf(",");
}
Zabuzard
  • 25,064
  • 8
  • 58
  • 82
cod3min3
  • 585
  • 1
  • 7
  • 23
  • 2
    You just said it. Do not set the `FileWriter` to `true` (as it then only appends new stuff). You need to read the whole file, save all lines (for example in a `List`). Then you manipulate the data. And after that you rewrite the **whole** text (for example by using the said `List`). However, if you exactly know which line needs to be added (and you don't need to find the place), then some more low-level File-Writer may be better for you. – Zabuzard Jul 25 '16 at 20:27
  • Would you do the same thing if the file will contains thousands of these lines? – cod3min3 Jul 25 '16 at 20:28
  • If you need to find the line, you will need to iterate through all lines. You may not need to save all lines (if the file is big). You can also remember the exact place and use a more low-level File-Writer. For example with `RandomAccessFile`: https://examples.javacodegeeks.com/core-java/io/randomaccessfile/java-randomaccessfile-example – Zabuzard Jul 25 '16 at 20:31
  • 1
    If the file had thousands of lines, you could read line by line from one file and write to a temp file then when you find the line with the change, make the change and write the rest to the temp file, then just copy that over to the original to overwrite it. – kamoroso94 Jul 25 '16 at 20:33
  • 1
    The bytes of the file are like a row of same-sized blocks ...if you remove one and replace it with a block of another size: do you see the problem? This is what you are trying to do. – ABuckau Jul 25 '16 at 20:37
  • If this help, as this is a CSV file you can use http://opencsv.sourceforge.net/apidocs/com/opencsv/CSVReader.html and read the file, whic will give you a List, run a for loop to find the key you want to change and update it and then use CSVWriter to write to a file. – lsiva Jul 25 '16 at 20:58

2 Answers2

2

You just said it. Do not set the FileWriter to true (as it then only appends new stuff). You need to read the whole file, save all lines (for example in a List<String>). Then you manipulate the data. And after that you rewrite the whole text (for example by using the said List<String>).

In your above code this would be first replace true with false:

BufferedWriter wr = new BufferedWriter(new FileWriter(file, false));

Second, always write to the file, as other lines would then get lost:

if(line.contains(id)) {
    ...
    line = line.replaceAll(extractText(line), newText); 
}
wr.write(line);

However, if you have a very big file and don't want to rewrite all lines, then you can go for a more low-level FileWriter, like RandomAccessFile. This concept allows you to start the file manipulation at a given position without rewriting everything that was before this position. You can search the line (or jump, if you know where), make your change. But you will need to rewrite everything that comes after the change. Here's an usage example: RandomAccessFile example

There is no better solution as this is platform dependent. More specific, it depends on the used Filesystem, for example NTFS or FAT32 and so on.
In general, they store a file by splitting it into several packages. The packages then get saved all over your hard drive, it puts them where they best fit in. The file system saves a pointer to each start package of a file in a master table. Every package then saves a pointer to the next packages and so on until EOF is reached.

You see, it is easy to change something in the middle without changing everything that was before. But you probably need to change the stuff that comes after, as you can not control how the OS splits the new data into packages. If you go very low-level, you may control a single packages and inject data without changing everything after. But I don't think that you want to do this :)

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
0

Unfortunately, there isn't really a way to do this (that I know of) besides loading all the file data into some mutable object, and then writing it all back to the file.

The Files class provides a convenient method for reading an entire file into a List


As @Zabuza mentioned in the comments, Java provides a class called RandomAccessFile, which can navigate to a specific place in a file and overwrite bytes. Methods that may be of interest to you would be:

Hope this helps

Graham
  • 7,431
  • 18
  • 59
  • 84
Jeeter
  • 5,887
  • 6
  • 44
  • 67
  • `RandomAccessFile` can read to an exact position by not changing stuff before (but after): https://examples.javacodegeeks.com/core-java/io/randomaccessfile/java-randomaccessfile-example – Zabuzard Jul 25 '16 at 20:32