2

我想要的结果是:

public class InterleavedBufferedReader extends BufferedReader {
...
}

并将其用作:

Reader[3] readers = ...; // three readers
InterleavedBufferedReader ibr = new InterleavedBufferedReader(readers);

ibr.readLine();  // this returns the first line of Reader 1
ibr.readLine();  // this returns the first line of Reader 2
ibr.readLine();  // this returns the first line of Reader 3

ibr.readLine();  // this returns the second line of Reader 1
ibr.readLine();  // this returns the second line of Reader 2
ibr.readLine();  // hey, Reader 3 had no more lines, return the third line of Reader 1

我尝试了什么(不好,我知道,这就是我在这里的原因):

public class InterleavedBufferedReader extends BufferedReader {

    static private int current = 0;
    private BufferedReader[] buffers;


    public InterleavedBufferedReader(BufferedReader[] in) {
        super(new StringReader("dummy"));
        buffers = in;
    }

    public InterleavedBufferedReader(Reader[] in) {
        super(new StringReader("dummy"));
        buffers = new BufferedReader[in.length];
        for (int i=0 ; i < in.length ; i++)
            buffers[i] = new BufferedReader(in[i]);
    }

    @Override
    public String readLine() throws IOException {
        int start = current;
        String line = null;
        while ((line = buffers[current].readLine()) == null) {
            current = (current+1) % buffers.length;
            if (current == start)
                return null;
        }       
        current = (current+1) % buffers.length;
        return line;

    }

    @Override 
    public void close() throws IOException {
        for (int i=0;  i < buffers.length; i++)
            buffers[i].close();
    }
}

注释:

  • 交错readLine()确实有效!
  • 也许我不应该延长BufferedReader。我不得不给它一个虚拟的 Reader 来管理,否则它甚至不会编译。同时,我重写了readLine()在儿童阅读器上调用它的方法。但是实现至少是不完整的,因为其他方法,如read(),实际上将引用我的虚拟读者。

我可以以更清洁的方式做到这一点吗?

4

2 回答 2

2

也许我不应该延长BufferedReader。我不得不给它一个虚拟的 Reader 来管理,否则它甚至不会编译。[...] 实现至少是不完整的,因为其他方法,如 read(),实际上将引用我的虚拟阅读器

您也可以并且应该覆盖read()(and ready())。readLine()必须保留该实现,但至少您确定所有读取方法都符合您希望实现的交错。无法避免虚拟阅读器,但您的实现可以。您甚至可能希望将 BufferedReader 的缓冲区设置为可以达到的最低值 (1),以减少内存浪费。

除此之外,您不能忘记流的标记支持:您必须通过覆盖mark()and重新实现它reset(),或者只是覆盖markSupported()它以使其始终返回 false。

于 2012-08-11T15:01:28.360 回答
1

在上面有用的评论之后,我正在回答我自己的问题,以展示我找到的解决方案,这似乎有效。我不确定它是否 100% 正确(未测试过长于缓冲区的行会发生什么)。

与第一版的区别:

  • 它延伸Reader,不是BufferedReader。它更简单,不需要虚拟阅读器。通过确保它的孩子是BufferedReader,它可以召唤readLine()他们。
  • 它仍然实现了 a readLine(),它提供了我正在寻找的功能
  • read(char[], int, int)使用readLine()上述实现。这样,我确信线条永远不会被打破。这是需要的,例如,new LineNumberReader(new InterleavedReader(...)).readLine()调用read(char[], int, int).InterleavedReader

这是代码。请让我知道您是否可以提出改进建议!

public class InterleavedReader extends Reader {

    private int current = 0;
    private BufferedReader[] buffers = null;
    private boolean skipLF = false;

    public InterleavedReader(Reader[] in) {
        super();
        buffers = new BufferedReader[in.length];
        for (int i=0 ; i < in.length ; i++) {
            if (in[i] instanceof BufferedReader)
                buffers[i] = (BufferedReader) in[i];
            else
                buffers[i] = new BufferedReader(in[i]);         
        }
    }

    public String readLine() throws IOException {
        // Every time, we issue readLine() to the next child BuffredReader
        // If we make a complete loop without a valid line,
        // then no child has more lines
        synchronized (lock) {
            int start = current;
            String line = null;
            while ((line = buffers[current].readLine()) == null) {
                current = (current+1) % buffers.length;
                if (current == start)
                    return null;
            }       
            current = (current+1) % buffers.length;
            return line;
        }
    }

    @Override
    public int read(char cbuf[], int off, int len) throws IOException {
        // To be sure we never break a line, we implement this using readLine()
        String s = readLine();
        if (s == null)
            return -1;
        System.arraycopy(s.toCharArray(), 0, cbuf, off, s.length());
        // readLine() doesn't include the '\n', append it to the buffer
        cbuf[off+s.length()] = '\n';
        return s.length()+1;
    }


    private int readChar() throws IOException {
        int start = current;
        int c = -1;
        while ((c = buffers[current].read()) == -1) {
            current = (current+1) % buffers.length;
            if (current == start)
                return -1;
        }   
        return c;
    }

    @Override
    public int read() throws IOException {
        synchronized (lock) {
            int c = readChar();
            if (skipLF && c == '\n') {
                c = readChar();
                skipLF = false;
            }
            switch (c) {
            case '\r':
                skipLF = true;
            case '\n':          /* Fall through */
                current = (current+1) % buffers.length;
                return '\n';
            }           
            return c;
        }
    }

    @Override
    public boolean markSupported() {
        return false;
    }

    @Override
    public boolean ready() throws IOException {
        synchronized (lock) {
            for(int i=0; i < buffers.length; i++)
                if (buffers[i].ready())
                    return true;
            return false;
        }
    }

    @Override 
    public void close() throws IOException {
        synchronized (lock) {
            for (int i=0;  i < buffers.length; i++)
                buffers[i].close();
        }
    }
}    
于 2012-08-11T21:04:32.030 回答