[HowTo] Replace things in a (config) file [Example: TABS to spaces]

Discussion in 'Resources' started by xGhOsTkiLLeRx, Dec 20, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    xGhOsTkiLLeRx

    Hello at all!

    I found myself searching for a solution to look through a config file and replace only some parts of it. Here I'll show you how to do!

    I'm going to create a method to "update" the config file.
    Example usage could be to scan for Tabs and to change them to 4 spaces!
    (Because every dev tries to tell the users not to use Tabs ;))


    Before we can start, we need to import something to read our file and to write a new file:
    Code:java
    1. import java.io.BufferedReader;
    2. import java.io.BufferedWriter;
    3. import java.io.File;
    4. import java.io.FileReader;
    5. import java.io.FileWriter;
    OR
    Code:java
    1. import java.io.*;


    First we need to prepare for the method with an initializing of a file. Add something like this in your source code (in this example we'll modify the config.yml):
    Make sure the config.yml exists at this point!
    Code:java
    1. configFile = new File(getDataFolder(), "config.yml");

    Maybe add this after you set up your config.
    Why do we need a real "File"?
    Because the method won't work with a FileConfiguration! ;)

    Now add (after you initialized the config first and then the configFile) this to your plugin. (Place it where you need it!)
    Code:java
    1. public void updateConfig(File config) throws Exception {
    2. BufferedReader reader = new BufferedReader(new FileReader(config));
    3. File tempFile = new File(getDataFolder(), "temp.txt");
    4. BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
    5. String line;

    What does this part do?
    Let's keep it simple.
    The second line creates a reader for the configFile.
    The part
    Code:java
    1. File tempFile = new File(getDataFolder(), "temp.txt");

    creates a temporary file (exactly: a new file), where we'll store our data.

    The fourth line sets up the writer, to write stuff into the tempFile.
    The fifth line creates a String called line. We need this one to see what each line contains.

    Now we can start searching for something special! :)
    (The fist code example contains a Tab! And it should be replaced by 4 spaces!)

    For example usage we will search for this:
    Code:
    name=COLOR
        name=COLOR
    and we want it to be replaced by
    Code:
    name: COLOR
        name: COLOR

    Here is how we do it!
    This method is case sensitive! Look below for a case insensitive method!
    Because we don't know if it fails to read and write the files, we need to try it.
    Code:java
    1. try {
    2. while ((line = reader.readLine()) != null) {
    3. writer.write(line.replace("\t", " ").replace("=", ": "));
    4. writer.newLine();
    5. }
    6. catch (Exception e) {
    7. log.warning("An error occurred! :(");
    8. e.printStackTrace();
    9. }
    10. finally {
    11. reader.close();
    12. writer.flush();
    13. writer.close();
    14. config.delete();
    15. tempFile.renameTo(config);
    16. }

    Woaah!! That looks complicated?!
    Nope. Very easy!

    The first part, the while part, is necessary, because we need to make sure were not at the end of the file!
    Now we are searching for a special key, the "=" and for a Tab ("\t" means Tab in Java).
    If one of them is found, or even both, we want to replace it!
    Code:java
    1. writer.write(line.replace("\t", " ").replace("=", ": "));
    2. writer.newLine();

    Now we need to update the line! We replace the equal sign with ": " and a Tab with 4 spaces (second ".replace") and write the line to our temporary created file.
    And we add a new line;)

    The last part is doing the rest.
    Code:java
    1. catch (Exception e) {
    2. log.warning("An error occurred! :(");
    3. e.printStackTrace();
    4. }
    5. finally {
    6. reader.close();
    7. writer.flush();
    8. writer.close();
    9. config.delete();
    10. tempFile.renameTo(config);
    11. }

    Because we are done with reading and writing, we "close" them. (The flush thingy clears the buffer)

    Now it's time to delete our old config and to rename the tempFile to the new config!
    Hey! We're done!

    The catch part is to see, if there are errors and to log (and display) them.

    Case insensitive method:

    Instead of the replace we can use replaceAll and add something to ignore the case sensitive, the (?i) part.
    Example:
    Code:java
    1. writer.replaceAll("(?i)red", "blue")
    This would replace all variants of the string red (that means: red, RED, rED, reD, Red, REd, etc.) with the string blue.
    Simple, isn't it?


    The full method could look like this:
    Code:java
    1. public void updateConfig(File config) throws Exception {
    2. BufferedReader reader = new BufferedReader(new FileReader(config));
    3. File tempFile = new File(getDataFolder(), "temp.txt");
    4. BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
    5. String line;
    6. try {
    7. while ((line = reader.readLine()) != null) {
    8. writer.write(line.replace("\t", " ").replace("=", ": "));
    9. writer.newLine();
    10. }
    11. catch (Exception e) {
    12. log.warning("An error occurred! :(");
    13. e.printStackTrace();
    14. }
    15. finally {
    16. reader.close();
    17. writer.flush();
    18. writer.close();
    19. config.delete();
    20. tempFile.renameTo(config);
    21. }
    22. }


    Thanks for reading!
    Post feedback, suggestions etc! :)
    Hope it helped you!
    Thanks @tips48 for the idea with a file reader!
    Thanks @garbagemule for the suggestions!
     
    tips48 likes this.
  2. You shouldn't need the the throws Exception part if you have a try catch :)
    Also, you could have a finally {} thing for the closing and cleanup (such as renaming of the files)
     
  3. Offline

    xGhOsTkiLLeRx

    I need the throws Exception for the BufferedReader/Writer.
    Or I could use a try again.

    Yeah, will add finally!
     
  4. Alright, cool :)
     
  5. Offline

    garbagemule

    Pretty decent walkthrough, although I do have a few comments.

    First of all, you say that "the while part, is necessary, because we don't want to use empty lines". This is not the reason for the structure of the while-loop's condition. What you're doing is taking whatever BufferedReader's readLine() method returns and putting it into 'line', and then you check if it is null. If the line is null, it means we've reached the end of the file (if a String is null, it is not the same as if it was empty; an empty String .equals("") or .isEmpty() (since Java 6)). Perhaps a bit nitpicky, but when it comes to teaching, it's important to get the facts straight :)

    Secondly, I misunderstood the intent of this whole thing. I thought you had come up with a nice way of saving only a part of a MemoryConfiguration to file, not to run through the file looking for things to replace. But I guess that's moreso me being stupid ;)

    Thirdly, your while-loop can be simplified (in my opinion). You basically do the exact same thing in both the if and the else, with the exception of creating a new String to throw into the writer.write() method. You can ditch almost half of the loop-body, and instead do this:
    PHP:
    while ((line reader.readLine()) != null) {
        if (
    line.contains("=")) {
            
    line line.replace("="": ");
        }
        
    writer.write(line);
        
    writer.newLine();
    }
    I think this makes the intent slightly more clear, in that "only if this condition holds, we do something extraordinary".

    *EDIT*: In fact, instead of the if, you could quite simply do the following:
    PHP:
    writer.write(line.replace("="": "));
    writer.newLine();
    While this is ultimately the shortest (and also the most efficient, if you want to be completely anal about optimization) solution, it also makes the code slightly less readable - especially for people new to programming. I just thought I'd throw it in there anyway :)

    Fourth, I think you could push the intent a little further. What exactly could this technique be useful for? Consider blissfully ignorant users, who fail to understand the warnings given by all plugin developers to "NOT USE TABS IN CONFIG FILES" - this technique is very useful for scanning through a config-file, looking for tab characters and replacing them with spaces. Example:
    PHP:
    if (line.contains("\t")) {
            
    line line.replace("\t""    ");
    }
    Other than that, very nice writeup, and very nice idea :)
     
    Twenty-Four and xGhOsTkiLLeRx like this.
  6. Offline

    xGhOsTkiLLeRx

    @garbagemule

    Thank you for your answer!
    First point: I meant the "if the file ends" and not new lines. Was too tired at 11 PM :p
    Second point: Will change the description/texts around the code a little bit
    Third point: I'll use the easier code from you! Thx!
    Fourth point: Yeah, that's a good usage! I'll add it! :)

    Again thanks for the feedback and optimization!
     
    garbagemule likes this.
  7. Offline

    xGhOsTkiLLeRx

    Added case insensitive method, little changes here and there (mostly spell fixes and grammar)
     
  8. Offline

    xGhOsTkiLLeRx

    Updated.
    Instead of renaming the new created temp file, it will now rename to the given argument from the method.
    Small edit, but I just noticed it while using my class.
     
  9. Offline

    messageofdeath

    Just use NotePad++ goto preferences (i think tabs one of them) and click replace tabs withspaces
     
  10. Offline

    xGhOsTkiLLeRx

    Of course you could do it like that. ;)
    I just write down an example:

    I used this in ColorMe from migration of the old 2.X to the new 3.X version.
    I have about 60.000 downloads. Imagine only one third (= 20.000) users will download and use it or whatever.

    So it's a bad idea to tell 20.000 people to open their configs with NotePad++ and replace things.

    Instead I wrote a little method which does this automatically for the user.
     
  11. Offline

    messageofdeath

    Oh i thought he was making a plugin that just replaces tabs with spaces.
     
  12. I found 1 problem, if the code is able to open the file for reading, bt the opening for writing failed, the file descriptor to the reader is never closed (= memory leak), this situation can happen if you restored the files from the server from an backup, and the backup is read-only
     
Thread Status:
Not open for further replies.

Share This Page