RobotRock
RobotRock

Reputation: 4459

InputStream returns unexpected -1/empty

I seem to be hitting a constant unexpected end of my file. My file contains first a couple of strings, then byte data.

The file contains a few separated strings, which my code reads correctly.

However when I begin to read the bytes, it returns nothing. I am pretty sure it has to do with me using the Readers. Does the BufferedReader read the entire stream? If so, how can I solve this?

I have checked the file, and it does contain plenty of data after the strings.

InputStreamReader is = new InputStreamReader(in);
BufferedReader br = new BufferedReader(is);
String line;
{
    line = br.readLine();
    String split[] = line.split(" ");

    if (!split[0].equals("#binvox")) {
        ErrorHandler.log("Not a binvox file");
        return false;
    }
    ErrorHandler.log("Binvox version: " + split[1]);
}

ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead, cnt = 0;
byte[] data = new byte[16384];

while ((nRead = in.read(data, 0, data.length)) != -1) {
    buffer.write(data, 0, nRead);
    cnt += nRead;
}

buffer.flush();
// cnt is always 0

The binvox format is as followed:

#binvox 1
dim 64 40 32
translate -3 0 -2
scale 6.434
data
[byte data]

I'm basically trying to convert the following C code to Java: http://www.cs.princeton.edu/~min/binvox/read_binvox.html

Upvotes: 1

Views: 289

Answers (5)

RobotRock
RobotRock

Reputation: 4459

I saw someone else's code that solved exactly this.

He/she used DataInputStream, which can do a readLine (although deprecated) and readByte.

Upvotes: 0

gaborsch
gaborsch

Reputation: 15758

I recommend to create a BinvoxDetectorStream that pre-reads some bytes

public class BinvoxDetectorStream extends InputStream {

    private InputStream orig;
    private byte[] buffer = new byte[4096];
    private int buflen;
    private int bufpos = 0;


    public BinvoxDetectorStream(InputStream in) {
        this.orig = new BufferedInputStream(in);
        this.buflen = orig.read(this.buffer, 0, this.buffer.length);
    }

    public BinvoxInfo getBinvoxVersion() {
        // creating a reader for the buffered bytes, to read a line, and compare the header
        ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
        BufferedReader rdr = new BufferedReader(new InputStreamReader(bais)));
        String line = rdr.readLine();
        String split[] = line.split(" ");
        if (split[0].equals("#binvox")) {
            BinvoxInfo info = new BinvoxInfo();
            info.version = split[1];
            split = rdr.readLine().split(" ");

            [... parse all properties ...]

            // seek for "data\r\n" in the buffered data
            while(!(bufpos>=6 &&
                    buffer[bufpos-6] == 'd' &&
                    buffer[bufpos-5] == 'a' && 
                    buffer[bufpos-4] == 't' && 
                    buffer[bufpos-3] == 'a' && 
                    buffer[bufpos-2] == '\r' && 
                    buffer[bufpos-1] == '\n') ) {
                bufpos++;
            }
            return info;

        }
        return null;
    }

    @Override
    public int read() throws IOException {
        if(bufpos < buflen) {
             return buffer[bufpos++];
        }
        return orig.read();
    }
}

Then, you can detect the Binvox version without touching the original stream:

BinvoxDetectorStream bds = new BinvoxDetectorStream(in);
BinvoxInfo info = bds.getBinvoxInfo();
if (info == null) {
    return false;
}
...
[moving bytes in the usual way, but using bds!!! ]

This way we preserve the original bytes in bds, so we'll be able to copy it later.

Upvotes: 0

msrd0
msrd0

Reputation: 8371

As icza has alraedy wrote, you can't create a InputStream and a BufferedReader and user both. The BufferedReader will read from the InputStream as many as he wants, and then you can't access your data from the InputStream.

You have several ways to fix it:

  1. Don't use any Reader. Read the bytes yourself from an InputStream and call new String(bytes) on it.
  2. Store your data encoded (e.g. Base64). Encoded data can be read from a Reader. I would recommend this solution. That'll look like that:
public byte[] readBytes (Reader in) throws IOException
{
    String base64 = in.readLine(); // Note that a Base64-representation never contains \n
    byte[] data = Base64.getDecoder().decode(base64);
    return data
}

Upvotes: 1

diogo38
diogo38

Reputation: 11

For reading the whole String you should do this:

ArrayList<String> lines = new ArrayList<String>();
while ((line = br.readLine();) != null) {
    lines.add(line);
}

and then you may do a cycle to split each line, or just do what you have to do during the cycle.

Upvotes: 1

icza
icza

Reputation: 417592

You can't wrap an InputStream in a BufferedReader and use both.

As its name hints, BufferedReader might read ahead and buffer data from the underlying InputStream which then will not be available when reading from the underlying InputStream directly.

Suggested solution is not to mix text and binary data in one file. They should be stored in 2 separate files and then they can be read separately. If the remaining data is not binary, then you should not read them via InputStream but via your wrapper BufferedReader just as you read the first lines.

Upvotes: 0

Related Questions