我想在一行中读取并处理包含单个OR多个命令的String流。
我目前正在使用InputStream in = socket.getInputStream();
作为我的输入流。
同样用于处理输入的典型线程:
public void run() {
String input = "";
try {
int data = 0;
while (!isInterrupted()) {
while ((data = in.read()) != -1 && !isInterrupted()) {
input += Integer.toHexString(data);
handleInput(input);
}
try {
sleep(500);
} catch (InterruptedException e) {
break;
}
}
socket.close();
return;
} catch (IOException e) {
main.log("Connection lost...");
main.log(e.toString());
main.stopBTCommunication();
main.startBTServer();
}
}
handleInput()
旨在处理任何给定的字符串并正确响应。这种实现的问题是,从handleInput()
读取的每个字节都会调用in.read()
。我知道,我可以使用BufferedReader.readLine()
,但这需要每个命令命令都附加"\n"
,但事实并非如此,也无法更改。
我知道
while (!isInterrupted()) {
while ((data = in.read()) != -1 && !isInterrupted()) {
是一种令人讨厌的东西,但基本上它希望线程读取,直到没有读取新内容,然后处理该输入然后再次读取...
编辑:
基本上,我需要的是非阻塞read()
。
EDIT2:
传入命令和命令链如何显示:
所以命令链可能看起来像下列之一:
"s"
"srlff"
= "s" + "rlff"
"rlffwlbb2e2e2e2erlbb"
= "s" + "rlff" + "wlbb2e2e2e2e" + "rlbb"
答案 0 :(得分:2)
我认为你真的不需要非阻塞读取。您需要一种逐字节读取流的方法,并将其转换为命令。
类似的东西:
public void processStream(InputStream in) {
List<Command> commands = new ArrayList<Command>();
while((int c = in.getChar()) != -1 ) {
switch((char)c) {
case 's':
commands.add(new SelectCommand());
break;
case 'r':
commands.add(ReadCommand.buildFromStream(in));
break;
case 'w':
commands.add(WriteCommand.buildFromStream(in));
break;
case ';':
commandEngine.execute(commands);
break;
default:
throw new StreamParseError("unexpected character: " + c);
}
}
}
这假定SelectCommand
,ReadCommand
,WriteCommand
与Command
类型兼容。
...例如ReadCommand.buildFromStream为:
public static ReadCommand buildFromStream(InputStream in) {
if((char)in.read() != 'n') {
throw new StreamParseError("Expect 'l' after 'r'");
}
// bad error checking here - be less lazy in real life.
String hexNum = in.read() + in.read();
int num = Integer.parseInt(hexNum,16);
return new ReadCommand(num);
}
这是非常原始的解析,但它显示了原理。有很好的技术可以进行更高级的解析,如果你愿意,可以阅读。
您还可以使用Scanner
。最常见的是,Scanner与分隔符一起使用,但它也可以查找正则表达式模式。
Scanner scanner = new Scanner(stream);
String cmd = "";
while(cmd != "e") { // I made up an "end" command :)
cmd = scanner.findWithinHorizon("(s|rl..|wl.{8}|e)",12);
if(cmd == null) {
// end of input, or badly formed input
break;
}
handleCmd(cmd);
}
答案 1 :(得分:1)
由于你的例子没有分隔符,我必须得到一些hack-y来证明扫描程序的真棒,但它仍然适用于你列出的确切命令。如果您期望命令词汇表发生变化,那将不是一个好的选择。
如果可能的话,我真的建议使用分隔符。它让生活更轻松。
如果我是你,我会看Scanner课。
Scanner
可以包装您的输入流,然后根据正则表达式或分隔符进行扫描以获取输入块。然后,您的handleInput()
方法可以对块(整个命令)进行操作,而不是单个字节。
以下是一个简短的独立示例:
package com.stackoverflow.q22199860;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.util.regex.Pattern;
public class ReadStream
{
public static void main(String[] args)
{
Pattern commandPattern = Pattern.compile("s|rl|wl");
String commands = "srlffwlbb2e2e2e2erlbb";
Charset utf8 = StandardCharsets.UTF_8;
try (
InputStream inputStream = new ByteArrayInputStream(commands.getBytes(utf8));
Scanner scanner = new Scanner(inputStream, utf8.name());
) {
scanner.useDelimiter(commandPattern);
while(scanner.hasNext()) {
String command = scanner.next();
if (command.isEmpty()){
//s
System.out.println("s" + command);
} else if (command.length() == 2) {
//rl
System.out.println("rl" + command);
} else if (command.length() == 10) {
//wl
System.out.println("wl" + command);
}
}
}
catch (IOException e)
{
System.err.println("Error Reading Stream");
}
}
}
这个输出是:
s
rlff
wlbb2e2e2e2e
rlbb
答案 2 :(得分:1)
你可以读取像这样的字节数组
int bytesRead = 0;
byte[] buffer = new byte[1024]; // reads up to 1024 byte chunks
while((bytesRead = in.read(buffer)) != -1) {
for ( int i = 0; i < bytesRead; i++ ) {
input += Integer.toHexString(buffer[i]);
}
handleInput(input);
}
上面的代码调用与旧代码相同,“输入”不断增长并反复使用调用handleInput()。不确定这是否是你的意图,但看起来很可疑。
答案 3 :(得分:1)
请注意,您正在从流中读取数据。这意味着您必须自己实现命令结构的恢复,即您必须至少在自己的代码中检测命令的开头和结尾。
这再次导致另一个问题:您无法保证传输层如何将流的数据拆分为“块”。您可以在一次read(buffer)
调用中收到一个命令加半个命令,然后在下一个read(buffer)
中接收命令的后半部分以及更多数据。
因此,我建议您只有在检测到一条消息/命令/结束时才继续读取数据,然后在读取更多传入数据并重复之前仅对此单条消息执行处理。其他一切(即处理部分收到的消息)很容易变得混乱。