Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending reader, how to return my "read"?

Tags:

java

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?

like image 440
Roei Jacobovich Avatar asked Oct 19 '22 12:10

Roei Jacobovich


2 Answers

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.

like image 159
dddsnn Avatar answered Oct 30 '22 23:10

dddsnn


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.

like image 26
batman Avatar answered Oct 31 '22 00:10

batman