字节[]输入到Java Scanner

时间:2013-11-22 19:33:14

标签: java java.util.scanner

我正在为Java应用程序编写插件。外部软件与此应用程序建立TCP连接,并将作为UTF-8编码的JSON对象的消息发送到我的插件。每条消息由分隔符分隔。我目前正在使用"\u00A1"¡)作为分隔符。

{ "message": "value" }¡{ "message": "value" }¡{ "message": "value" }...

由于TCP不能保证一次有多少数据到达,因此插件必须接收此数据流并提取每个{ "message": "value" }令牌。听起来好像java.util.Scanner ..

问题是,应用程序没有提供我的插件直接访问TCP套接字。该插件在重复调用receiveData(byte[] bytes)方法时接收数据。我需要Scanner可以读取的某种输入流或通道,但我也可以将字节存入(来自receiveData)。这样的事情存在吗?如果没有,任何实施建议?或者我离开了,是否有更好的方法来解决这个问题?

注意:我最初尝试手动实现此逻辑。我将获取每个接收字节块,解码为字符串,搜索分隔符,并附加到StringBuilder。然后我意识到这种方法无效,因为传入的byte[]可能不会以偶数UTF-8字符边界结束并且无法正确解码。我觉得Scanner正是我想要的,我只是无法弄清楚如何提供输入。

编辑:在应用程序运行时,数据会连续流式传输以更新显示。不可能等到没有更多数据开始解析。

1 个答案:

答案 0 :(得分:0)

我最终创建了一个循环缓冲区,其中包含一个实现ReadableByteChannel的条件变量。似乎工作得很好。这是一个完整的例子:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Scanner;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Buffer implements ReadableByteChannel {

    private final Lock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();

    private int readIndex = 0;
    private int writeIndex = 0;
    private int size = 0;

    private final int capacity;
    private final byte[] buff;

    public Buffer(int capacity) {
        this.capacity = capacity;
        this.buff = new byte[capacity];
    }

    /**
     * Deposit bytes to the buffer. Will only write until
     * buffer is full.
     * @param bytes the bytes to add
     * @return the number of bytes actually added
     */
    public int addBytes(byte[] bytes) {
        lock.lock();
        int writeCount = 0;
        try {
            int available = capacity - size;
            writeCount = available <= bytes.length ? available : bytes.length;
            for (int i = 0; i < writeCount; ++i) {
                buff[writeIndex] = bytes[i];
                incrementWrite();
                ++size;
            }
            // notify callers waiting on read()
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
        return writeCount;
    }

    public int addBytes(byte[] bytes, int offset, int length) {
        lock.lock();
        int writeCount = 0;
        try {
            int available = capacity - size;
            writeCount = available <= length ? available : length;
            for (int i = 0; i <writeCount; ++i) {
                buff[writeIndex] = bytes[offset + i];
                incrementWrite();
                ++size;
            }
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
        return writeCount;
    }

    @Override
    public int read(ByteBuffer byteBuffer) throws IOException {
        lock.lock();
        try {
            // if the current size is 0, wait until data is added
            while (size == 0) {
                notEmpty.wait();
            }
            int attempt = byteBuffer.remaining();
            int readCount = attempt <= size ? attempt : size;
            for (int i = 0; i < readCount; ++i) {
                byteBuffer.put(buff[readIndex]);
                incrementRead();
                --size;
            }
            return readCount;
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            return 0;
        } finally {
            lock.unlock();
        }
    }

    @Override
    public boolean isOpen() {
        return true;
    }

    @Override
    public void close() throws IOException {
        // do nothing
    }

    private final void incrementRead() {
        // increment and wrap around if necessary
        if (++readIndex == capacity) {
            readIndex = 0;
        }
    }

    private final void incrementWrite() {
        // increment and wrap around if necessary
        if (++writeIndex == capacity) {
            writeIndex = 0;
        }
    }

    public static void main(String[] args) {
        final Buffer buff = new Buffer(1024);
        final Scanner scanner = new Scanner(buff).useDelimiter("!");

        Thread readThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (scanner.hasNext()) {
                    String message = scanner.next();
                    System.out.println(message);
                    if (message.equals("goodbye")) {
                        return;
                    }
                }
            }
        });
        readThread.start();

        buff.addBytes("hello,".getBytes());
        buff.addBytes(" world!".getBytes());
        buff.addBytes("good".getBytes());
        buff.addBytes("bye!".getBytes());
    }
}