将整个nio Channel读入String

时间:2017-07-10 17:51:07

标签: java nio

我需要在字符串中读取ReadableByteChannel(例如FileChannel)。我不能通过使用Channels.newReader()Channels.newInputStream()封装频道来实现此目的,因为我不希望在收集包装时关闭频道。我也不想将所有字节存储在中间ByteBuffer

我想在循环中读取输入并调用CharsetDecoder.decode(ByteBuffer, CharBuffer, boolean),但问题是它在填满时不会重新分配CharBuffer。解决方案看起来太复杂了:

public static CharBuffer readAll(final ReadableByteChannel ch, final Charset cs)
        throws IOException {
    final ByteBuffer bb = ByteBuffer.allocate(3);
    CharBuffer cb = null;

    final CharsetDecoder dec = cs.newDecoder();
    while (ch.read(bb) > 0) {
        bb.flip();
        cb = mydecode(dec, cb, bb);
        bb.clear();
    }
    bb.flip();
    dec.decode(bb, cb, true);
    cb.flip();
    return cb;
}

// modified CharsetDecoder.decode(ByteBuffer)
private static CharBuffer mydecode(final CharsetDecoder dec, CharBuffer out, final ByteBuffer in)
        throws CharacterCodingException {
    int n = (int) (in.remaining() * dec.averageCharsPerByte());
    if (out == null) {
        out = CharBuffer.allocate(n);
    }

    if ((n == 0) && (in.remaining() == 0)) {
        return out;
    }
    // dec.reset();
    for (;;) {
        final CoderResult cr = in.hasRemaining() ? dec.decode(in, out
        // , true
                , false) : CoderResult.UNDERFLOW;
        if (cr.isUnderflow()) {
            // cr = dec.flush(out);
        }

        if (cr.isUnderflow()) {
            break;
        }
        if (cr.isOverflow()) {
            n = 2 * n + 1; // Ensure progress; n might be 0!
            final CharBuffer o = CharBuffer.allocate(n);
            out.flip();
            o.put(out);
            out = o;
            continue;
        }
        cr.throwException();
    }
    // out.flip();
    return out;
}

2 个答案:

答案 0 :(得分:0)

我不知道任何声明或证据表明包装Reader或InputStream会在收集垃圾时自行关闭(以及它包装的Channel)。

但如果您真的想要其他方式,可以使用扫描仪:

public static String readAll(final ReadableByteChannel ch,
                             final Charset cs)
throws IOException {
    Scanner s = new Scanner(ch, cs.name());
    String text = s.useDelimiter("\\z").next();

    IOException e = s.ioException();
    if (e != null) {
        throw e;
    }

    return text;
}

答案 1 :(得分:0)

  

我无法通过使用Channels.newReader()Channels.newInputStream()

打包频道来实现此目的

是的,你可以。

  

因为我不希望在收集包装器时关闭通道

不会。在对基础FileDescriptor的最后一次引用进行垃圾收集之前,或者您自己特意关闭它之前,通道不会关闭。

您似乎也不了解CharBuffer CharsetDecoder.decode(ByteBuffer in)

你的问题完全没有动力。