【问题标题】:How to scan a slow InputStream with minimal delay?如何以最小的延迟扫描慢速 InputStream?
【发布时间】:2024-04-12 09:20:01
【问题描述】:

这是一个模拟慢流的模拟InputStream

class SlowInputStream extends InputStream{
    private String internal = "5 6\nNext Line\n";
    private int position = 0;

    @Override
    public int available(){
        if(position==internal.length()) return 0;
        else return 1;
    }

    @Override
    public int read() throws IOException {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new IOException("Sleep Interrupted");
        }
        if(position==internal.length()) return -1;
        return internal.charAt(position++);

    }

}

这里是测试代码:

    Scanner s = new Scanner(new SlowInputStream());

    int i=s.nextInt();
    System.out.println("i="+i);
    int j=s.nextInt();
    System.out.println("j="+j);
    s.nextLine();
    String line = s.nextLine();
    System.out.println("line="+line);

    s.close();

上述代码的行为是它会停止一段时间并打印三行。什么代码可以输出相同的东西,但在两行之间分割等待时间?

【问题讨论】:

    标签: inputstream java.util.scanner java-io


    【解决方案1】:

    您还需要覆盖 read(byte[], int, int) 。你最好扩展FilterInoutStream:这就是它的用途。

    【讨论】:

    • 覆盖read 是不够的。 Scanner 将继续调用它,直到它接收到 4096 个字节或到达我机器中流的末尾。这同样适用于FilterInoutStream
    【解决方案2】:

    如果Scanner 是从InputStream 创建的,它将尝试每次读取特定数量的数据,以提高性能。在我的机器中,数量是 4096 字节。这就是为什么在这个例子中它总是一直等到同时打印所有结果。

    要绕过这个缓存机制接口Readable应该使用而不是原始流。因为没有已知的实现可以这样做,所以必须实现自己的。 (如果有人知道这样的事情,请告诉我)所以下面的代码可以完成这项工作:

    Scanner s = new Scanner(new Readable(){
    
        private InputStream is = new SlowInputStream();
    
        public int read(CharBuffer arg0) throws IOException {
            int i = is.read();
            if(i>=0){
                arg0.append((char)i);
                return 1;
            } else return -1;
        }
    
    });
    
    int i=s.nextInt();
    System.out.println("i="+i);
    int j=s.nextInt();
    System.out.println("j="+j);
    s.nextLine();
    String line = s.nextLine();
    System.out.println("line="+line);
    
    s.close();
    

    编辑:上述解决方案有一些缺点。首先,它不会关闭内部输入流,因此存在泄漏。其次,它取决于字符集是单字节的。它不适用于多字节字符集(即 UTF-8)。为了解决这些问题,我们可以改用ReadableByteChannel

    Scanner s = new Scanner(new ReadableByteChannel(){
    
        private InputStream is = new SlowInputStream();
        boolean isopen = true;
    
        public int read(ByteBuffer arg0) throws IOException {
            int i = is.read();
            if(i>=0){
                arg0.put((byte)i);
                return 1;
            } else return -1;
        }
    
        public void close() throws IOException {
            isopen=false;
            is.close();
        }
    
        public boolean isOpen() {
            return isopen;
        }
    
    });
    

    编辑:感谢您的评论,我已经修复了文本中的一个错误,并对这个问题给出了更好的解释。

    【讨论】:

    • 您到底在说什么“输入流的默认缓存”?为什么 Readable 能解决这个想象中的问题?
    • 我认为这是一个错误。缓存实际上是由Scanner 使用的。如果扫描仪是从InputStream 创建的,它将尝试每次读取特定数量(在我的情况下为 4096 字节)的数据。所以在这个例子中,它会一直读取,直到所有数据都被接收到。通过使用ReadableReadableByteChannelScanner 将只在需要时读取字节。
    最近更新 更多