【问题标题】:InputStream returns unexpected -1/emptyInputStream 返回意外的 -1/空
【发布时间】:2014-08-08 14:05:10
【问题描述】:

我的文件似乎经常出现意外结束。我的文件首先包含几个字符串,然后是字节数据。

该文件包含几个单独的字符串,我的代码可以正确读取。

但是,当我开始读取字节时,它什么也没有返回。我很确定这与我使用阅读器有关。 BufferedReader 是否读取整个流?如果是这样,我该如何解决?

我检查了文件,它确实包含字符串后面的大量数据。

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

binvox格式如下:

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

我基本上是在尝试将以下 C 代码转换为 Java: http://www.cs.princeton.edu/~min/binvox/read_binvox.html

【问题讨论】:

标签: java


【解决方案1】:

要阅读整个String,您应该这样做:

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

然后你可以做一个循环来分割每一行,或者只是做你在循环中必须做的事情。

【讨论】:

    【解决方案2】:

    正如 icza 已经写的那样,您不能同时创建 InputStreamBufferedReader 和用户。 BufferedReader 会从InputStream 读取任意数量的数据,然后您将无法从InputStream 访问您的数据。

    你有几种方法可以解决它:

    1. 不要使用任何Reader。自己从InputStream 读取字节并调用new String(bytes)
    2. 存储您的数据编码(例如 Base64)。可以从Reader 读取编码数据。我会推荐这个解决方案。看起来像这样:
    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
    }
    

    【讨论】:

    • 我不能只获取 char 数据并将其转换为字节吗?
    • @RobotRock 这正是我在那里写的——除了它是 base64 编码的。如果未编码(无论如何),您将无法从不同平台交换文件,因为它们将具有不同的编码
    • 我对文件的规范没有发言权。您在上面所做的是将字符串获取为 base64 字节。但是我想知道,如果我将字符串作为字符数组获取,是否可以将每个字符转换为字节。
    • @RobotRock 这些字符以字节表示,这是正确的,但几乎每个操作系统都使用另一种编码(UTF-8、ISO-8859-15,...),因此存储数据更加困难那里
    【解决方案3】:

    您不能将 InputStream 包装在 BufferedReader 中并同时使用两者。

    正如其名称所暗示的,BufferedReader 可能会提前读取并缓冲来自底层 InputStream 的数据,当直接从底层 InputStream 读取时,这些数据将不可用。

    建议的解决方案是不要将文本和二进制数据混合在一个文件中。它们应该存储在 2 个单独的文件中,然后可以单独读取。如果剩余的数据不是二进制的,那么你不应该通过InputStream 来读取它们,而是通过你的包装器BufferedReader 来读取它们,就像你读取第一行一样。

    【讨论】:

    • 问题是您在一个文件中混合了文本数据和二进制数据。你不应该那样做。如果你不这样做,你可以单独阅读它们。
    • 我没有写文件的规范。
    • 无论是谁写的,那么文件的其余部分很可能不是二进制文件,而是文本文件。您应该使用相同的BufferedReader 继续阅读其余部分,而不是通过底层的InputStream
    【解决方案4】:

    我建议创建一个预读一些字节的BinvoxDetectorStream

    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();
        }
    }
    

    然后,您可以在不接触原始流的情况下检测 Binvox 版本:

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

    这样我们保留了bds 中的原始字节,以便我们以后可以复制它。

    【讨论】:

      【解决方案5】:

      我看到别人的代码正好解决了这个问题。

      他/她使用了 DataInputStream,它可以执行 readLine(虽然已弃用)和 readByte

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-06-03
        • 2015-05-11
        • 2017-01-12
        • 1970-01-01
        • 2023-03-22
        • 1970-01-01
        • 2018-08-06
        相关资源
        最近更新 更多