我有一个程序,它将通过System.in从外部源接收信息。有两种输入模式:线路模式和原始模式。在行模式下,输入只是一系列UTF-8字符串,每个字符串都以换行符结束。在线路模式下的某个时刻,我将收到通知,即我将接收N字节的原始数据。此时输入切换到原始模式,我接收到N字节的原始二进制数据,这些数据不是有效的UTF-8字符。在此之后,它将返回到行模式。
有没有办法轻松切换读取字符串和读取原始数据?我唯一的想法是逐字节读取一个InputStream并在我去的时候转换为字符。有没有办法用多种类型的输入流包装System.in?我觉得从两个不同的包装中读取会导致问题。
(已修复)更新:
我尝试过parsifal的建议,但遇到了问题。为了模拟切换输入模式,我修改了我的测试工具。 (我意识到我的另一个进程最终也需要以这种方式输出。)我不知道问题是由发送端还是接收端引起的。当我在输出模式之间切换时,它似乎没有正确读取字节。此外,它始终显示相同的字节值。以下是一些代码摘录:
FIX:问题是显然你不能太快地从OutputStreamWriter切换到OutputStream。我在发送原始字节之前添加了1ms的sleep命令,问题解决了!
测试工具:
Process p = processList.get(pubName); //Stored list of started Processes
OutputStream o = p.getOutputStream(); //Returns OutputStream which feeds into stdin
out = new OutputStreamWriter(runPublisher.getOutputStream());
byte[] payload = new byte[25];
out.write("\nPAYLOAD\nRAW\n"); // "RAW\n" signals raw mode
out.write(String.valueOf(payload.length) + "\n");
out.flush();
Thread.sleep(1); //This fixed the problem I was having.
System.out.println(Arrays.toString(payload));
o.write(payload);
o.flush();
客户端:
InputStreamReader inReader = new InputStreamReader(System.in);
while(true){
try{
if((chIn = inReader.read())!= -1){
if(chIn == (int)'\n'){
if(rawMode){
if(strIn.equals("ENDRAW"))
rawMode = false;
else{
System.out.println(strIn);
//Exception on next line
int rawSize = Integer.parseInt(strIn);
payload = new byte[rawSize];
int t = System.in.read(payload);
System.out.println("Read " + t + " bytes");
System.out.print(Arrays.toString(payload));
}
}else if(strIn.startsWith("RAW")){
rawMode = true;
}else {
// Do other things
}
strIn = "";
}else
strIn += (char)chIn;
}else
break;
}catch(IOException e){break;}
}
输出(在添加Sleep语句之前)如下所示:
测试线束:
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ]
客户端:
25个
读取9个字节
[83,72,85,84,68,79,87,78,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]
Exception in thread "main" java.lang.NumberFormatException: For input string: "
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:470)
at java.lang.Integer.parseInt(Integer.java:514)
at myClass.handleCommand(myClass.java:249)
答案 0 :(得分:3)
您可以使用指定“utf-8”编码的System.in
包装InputStreamReader
,然后逐个字符地读取。将字符累积到StringBuilder
并在适当的时候发送(当您看到'\n'
时,这可能是基于建筑商的测试)。
当您想要读取二进制数据时,只需从基础InputStream
(System.in
)中读取。 InputStreamReader
根据需要执行转换,不会缓冲数据。
你不想要在堆栈中使用任何类型的缓冲流或读取器。这将消除使用readLine()
方法的任何机会,至少如果您只限于JDK类。
根据您的最新更新进行修改:
我认为你在原始模式和熟食模式之间切换有点可疑。如果我要实现这一点,我将创建两个原始操作String readLine()
和byte[] readData(length)
。第一个字符累积到换行符,第二个读取固定缓冲区。然后你的主循环看起来像这样:
InputStream in = // ...
Reader rd = new InputStreamReader(in, "USASCII"); // or whatever encoding you use
while (true) {
String command = readLine(rd );
if (command .equals("RAW")) {
int length = Integer.parseInt(readLine(rd ));
byte[] data = readData(in , length);
if (! readLine(rd ).equals("ENDRAW")) {
throw // an exception that indicates protocol violation
}
}
else // process other commands
}
我还要将整个事物包装在一个围绕流构建的对象中,并且可能使用回调来分派数据包。
答案 1 :(得分:1)
最好的选择可能是逐字节(使用System.in.read()
)读入缓冲区,直到您点击UTF-8换行字节0x0A,然后将该字节缓冲区转换为字符串(使用{ {1}})。
请注意,调用InputStream的new String(byte[] bytes, "UTF-8")
将返回一个值为0到255的int,您需要将其转换为一个字节。您可以在某种类型的Collection中累积字节,然后使用标准的Collection框架工具将其转换为数组以供String构造函数使用。
当你看到它要切换的指示器(可能是某种插入信号,某些特定字节)时,然后切换到原始字节读取代码。