0

I'm trying to make a compressor for TextFiles and I get stuck at replacing characters.

This is my code:

compress.setOnAction(event ->
    {
        String line;
        try(BufferedReader reader = new BufferedReader(new FileReader(newFile)))
        {
            while ((line = reader.readLine()) != null)
            {
                int length = line.length();
                String newLine = "";


                for (int i = 1; i < length; i++)
                {
                    int c = line.charAt(i);

                    if (c == line.charAt(i - 1))
                    {

                    }
                }
            }

        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    });

So what I want to do is: I want to find all the words where two characters are equal, if they are aside (Like 'Took'). When the if statement is true, I want to replace the first letter of the two equals characters, so it would look like: 'T2ok'.

I've tried a lot of things and I get an ArrayOutOfbounds, StringOutOfbounds, and so on, all the time...

Hope someone has a great answer :-)

Regards

Alexander Falk
  • 499
  • 8
  • 21
  • Strings in java are inmutable objects, so: **the answer to your problem is to use [`StringBuilder`](https://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html) instead of `String`**. [Check this answer](http://stackoverflow.com/questions/3472663/replace-all-occurences-of-a-string-using-stringbuilder) for further info. – Jordi Castilla Jan 14 '16 at 11:21
  • 1
    Got curious by the Alice part of title, what's that about? – zubergu Jan 14 '16 at 12:06

2 Answers2

1

Create a method that compress one String as follows:
Loop throu every character using a while loop. Count the duplicates in another nested while loop that increments the current index while duplicates are found and skips them from being written to output. Additionally this counts their occurence.

public String compress(String input){
    int length = input.length(); // length of input
    int ix = 0;                  // actual index in input
    char c;                      // actual read character
    int ccounter;                // occurrence counter of actual character
    StringBuilder output =       // the output
            new StringBuilder(length);

    // loop over every character in input
    while(ix < length){
        // read character at actual index then inc index
        c = input.charAt(ix++);
        // we count one occurrence of this character here
        ccounter = 1;
        // while not reached end of line and next character
        // is the same as previously read
        while(ix < length && input.charAt(ix) == c){
            // inc index means skip this character
            ix++;
            // and inc character occurence counter
            ccounter++;
        }
        // if more than one character occurence is counted
        if(ccounter > 1){
            // print the character count
            output.append(ccounter);
        }
        // print the actual character
        output.append(c);
    }
    // return the full compressed output
    return output.toString();
}

Now you can use this method to create a file input to output stream using java8 techniques.

// create input stream that reads line by line, create output writer
try (Stream<String> input = Files.lines(Paths.get("input.txt"));
     PrintWriter output = new PrintWriter("output.txt", "UTF-8")){
    // compress each input stream line, and print to output
    input.map(s -> compress(s)).forEachOrdered(output::println);
} catch (IOException e) {
    e.printStackTrace();
}

If you really want to. You can remove the input file and rename the output file afterwards with

Files.move(Paths.get("output.txt"), Paths.get("input.txt"),StandardCopyOption.REPLACE_EXISTING);

I think this is the most efficient way to do what you want.

ArcticLord
  • 3,999
  • 3
  • 27
  • 47
  • I see the idea, but I'm a bit confused what you are doing here? Two while loops with ix < length? Could you specify it for me, and thanks in advance :) – Alexander Falk Jan 14 '16 at 11:51
  • 1
    added some comments. Hope this helps. – ArcticLord Jan 14 '16 at 12:04
  • How would you then add the changes to the file? I want it to be like: T2OK, instead of TOOK. Hope you can see my point :) – Alexander Falk Jan 15 '16 at 09:11
  • Dear ArcticLord. That answer was really great! Thank you so much. Love the explanation, so I could follow through and wasn't lost in translation. It worked out. Though didn't the file seem to shrink, so I'm on the road again to come up with a better self-made idea :D. Have a nice weekend. – Alexander Falk Jan 15 '16 at 15:52
1

try this:

    StringBuilder sb = new StringBuilder();
    String line;
    try(BufferedReader reader = new BufferedReader(new FileReader(newFile)))
    {
        while ((line = reader.readLine()) != null)
        {

            if (!line.isEmpty()) {

                //clear states
                boolean matchedPreviously = false;
                char last = line.charAt(0);

                sb.setLength(0);
                sb.append(last);

                for (int i = 1; i < line.length(); i++) {
                    char c = line.charAt(i);

                    if (!matchedPreviously && c == last) {

                        sb.setLength(sb.length()-1);
                        sb.append(2);

                        matchedPreviously = true;
                    } else matchedPreviously = false;

                    sb.append(last = c);
                }
                System.out.println(sb.toString());
            }
        }

    }
    catch (IOException ex)
    {
        ex.printStackTrace();
    }

This solution uses only a single loop, but can only find occurrences of length 2

Gilfoyle
  • 301
  • 1
  • 10
  • I believe this is a great solution. How would you save this to the File, that we're reading? :) – Alexander Falk Jan 14 '16 at 13:48
  • The easiest way would be to write the new data to a new file and switch the files at the end. But I guess you want to do a streaming edit? – Gilfoyle Jan 14 '16 at 14:08
  • A streaming edit would be nice, unless it's hard to do that? I've made it in JavaFX, and made a ugly button which compress the file, when I press it. That's where the code above appears. – Alexander Falk Jan 14 '16 at 14:57
  • 1
    You can use RandomAccessFile to simultaniously read and write a file but you would have to calculate the positions of the changes yourself and abstain from using BufferedReader. – Gilfoyle Jan 14 '16 at 15:15