5

As part of my Java course, I wrote a "zip" writer and reader - as Huffman algorithm works.

My class extends Reader, and have an object Reader r. In my main method, I have these lines:

input = new BufferedReader(new HuffmanReader(new FileReader("output.hff")));
String str = input.readLine();

It should return the decompressed string I wrote to the file, after decompressing it, of course. But it returns the first line of the file!

My read function:

public int read(char[] cbuf, int off, int len) throws IOException {
    //...
    r.read(buffer,0,8192)
    //does the decompress process
    String fnlStr = ... //The final result
    cbuf = fnlStr.toCharArray();
    //close streams
    return cbuf.length;
}

My Debug window shows this:

HuffmanReader.read(char[], int, int) line: 23   
BufferedReader.fill() line: not available   
BufferedReader.readLine(boolean) line: not available    
BufferedReader.readLine() line: not available   
Run.main(String[]) line: 23

It calls my read function twice. How can I stop the bufferReader from calling the read function again?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 1. Do you consider `off` and `len` in your `read()` function? 2. Do you need that BufferedReader (can't you test without it)? – xerx593 Apr 30 '15 at 21:25
  • 1
    The only place where you read from `input` is in `input.readLine()`, which, naturally, only reads one line. I'm a little surprised that your `read` function works at all, because you don't actually write to the buffer that's passed in, you just assign a new buffer to `cbuf`. – dddsnn Apr 30 '15 at 21:37
  • You also don't make any use of the count returned by `r.read().` This code cannot possibly work. – user207421 May 01 '15 at 01:03
  • When I'm reading from the file i'm reading into temporary buffer until 8192 chars. It there more than 8192, it copies it to the string passed to the decompress function and read the next chars. I can't change the main - as part of the exercise. I don't know how to write to my BufferedReader - that's what i'm asking for. – Roei Jacobovich May 01 '15 at 07:23

2 Answers2

2

You don't return the data you read from the method like you would usually. Instead, when read is called, the caller gives you the array cbuf, which is essentially the address of a chunk of memory, and tells you to write len chars into it.

When you do cbuf = fnlStr.toCharArray(), you're just replacing your local copy of that address with another address, but you're not actually changing the memory you were supposed to write to. You need to either iterate over the array you are given in a for loop and write to it, or use System.arraycopy if you have constructed another buffer that contains the result.

E.g., the following read method will always read "Test\n":

public int read(char[] cbuf, int off, int len) throws IOException {
    char[] result = "Test\n".toCharArray();
    int numRead = Math.min(len, result.length);
    System.arraycopy(result, 0, cbuf, off, numRead);
    return numRead;
}

Replacing the "Test\n" literal with your decompressed string should get you started. Of course, you will still have to manage how much of your source you have already consumed.


And as to BufferedReader calling read twice: you shouldn't care how often it's called. Simply get the data from your underlying source, write it to cbuf and return the number of chars you have written. If there is nothing left to read, return -1 to signal the end of the stream (in which case BufferedReader will stop calling read).


As an aside, Reader is meant to read character streams, while InputStream is for binary data (it's basically the same thing, just with byte[] instead of char[] and without using a charset). Since compressed files are binary, you might want to switch your FileReader to a FileInputStream.

I could imagine weird bugs if, for some reason, the charset you encode with isn't the same you decode with. Or less dramatically, you might use more space than you think, if one 16-bit code unit in UTF-16 needs 3 8-bit code units in UTF-8.

dddsnn
  • 2,231
  • 1
  • 12
  • 6
  • Thank you very much! That was my problem. It works well! I know about the problem with the Reader, it's a limit we had for this exercise. Thank you. – Roei Jacobovich May 02 '15 at 17:08
1

You are reading only the first line. Change the first part to something like:

input = new BufferedReader(new HuffmanReader(new FileReader("output.hff")));
Arraylist<String> list = new ArrayList<String>();
String line;

while ((line = reader.readLine()) != null) {
    list.add(line);
}

And also, to fix that your method is being called twice, make a boolean and set it to true after you have done your things in the method. Then in the beginning of that method, check if that boolean is true. If it is, return from the method so it won't be executing things after it again.

batman
  • 90
  • 1
  • 10
  • Or just call the method once ;-) – user207421 May 01 '15 at 07:10
  • @ImBatman64 Hi, I tried earlier the boolean thing and it didn't work, because I don't know how to write my result to the buffer. When it comes the next time, it returns to String str a string with some spaces. I can't change the main part - it's part of the exercise. The BufferedReader is trying to read again, although it should read only one line. – Roei Jacobovich May 01 '15 at 07:27
  • @RoeiJacobovich Where are you exactly calling the method? – batman May 01 '15 at 07:46
  • @ImBatman64 just as I wrote above for the main part. My input.readLine() calls my read fuction twice. – Roei Jacobovich May 01 '15 at 07:55
  • RoeiJacobovich Can you post the full source code somewhere so I can test it? It would help alot. – batman May 01 '15 at 08:02
  • What is inside the "out.hff" file? – batman May 01 '15 at 08:30
  • The compressed string. It's not the problem - the problem is that I can't write to the bufferedReader the fnlStr. – Roei Jacobovich May 01 '15 at 08:43
  • @RoeiJacobovich http://pastebin.com/rbiM3bXz There is a "fixed" version of it (hopefully). Now it should not be calling the method twice, but only once. – batman May 01 '15 at 08:53