2013-01-07 100 views
0

我有一个程序,它将通过System.in从外部源接收信息。有两种输入模式:行模式和原始模式。在行模式下,输入只是一系列UTF-8字符串,每个字符串都以换行符结尾。在线路模式下,我会收到通知,说我即将收到N字节的原始数据。此时输入切换到原始模式,并且我会收到N个字节的原始二进制数据,这些数据不是有效的UTF-8字符。在此之后,它返回到行模式。从java中的标准输入读取字符串和原始字节

有没有方法可以轻松地切换阅读字符串和阅读原始数据?我唯一的想法是逐字节地读取一个InputStream字节,并随时转换为字符。有没有什么方法可以将System.in换成多种类型的输入流?我觉得从两个不同的包装纸上读取会导致问题。

(固定)更新:

我试图帕西法尔的建议,但我遇到了一个问题。为了模拟开关输入模式,我修改了测试线束。 (我意识到我拥有的另一个流程最终也需要以这种方式输出。)我不知道问题是由发送端还是接收端引起的。当我在输出模式之间切换时,它似乎没有正确读取字节。而且,它总是出现相同的字节值。这里有一些代码片段:

修复:问题是,显然你不能从OutputStream快速切换到OutputStream。在发送原始字节之前,我添加了1ms睡眠命令,并且问题得以解决!

测试工具:

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;} 
} 

,输出(前要添加休眠语句)是这样的:

测试工具:
[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]

客户:
读取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]

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) 
+1

用户如何输入原始字节0x00通过键盘?! –

+0

你能保证在原始字节之前换行吗? – parsifal

+0

@YasserZamani,它不是来自键盘。它由另一个程序控制,并且命令通过stdin传入。 –

回答

3

您可以用System.in换行,InputStreamReader指定“utf-8”编码,然后逐字符读取。将字符累积到StringBuilder并在适当的时候发送(当您看到'\n',但可能基于对构建器的测试时才会发送)。

当您想读取二进制数据时,只需从底层的InputStreamSystem.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 
} 

我也想包了整个事情了一个对象,这是流围绕构建,也许使用回调分派的数据包。

+0

我会试试这个方法,并且如果它有效,这可能是一段时间)。感谢缓冲技巧 - 我不会想到这一点。 –

+0

试过这个并遇到问题。你可以看看我的更新,看看你的想法? –

+0

当我在自己的Java进程之间进行通信时,这起作用,但是当我尝试与外部源进行通信时,我不得不切换到从System.in逐字节读取 –

1

最好的选择可能是(使用System.in.read())刚读逐字节到缓冲区中,直到你遇到UTF-8号线提供字节0x0A,然后将该字节缓冲区转换为字符串(使用new String(byte[] bytes, "UTF-8"))。

注意到read()呼吁为InputStream将返回一个int从0到255之间的值,你需要将它转换成字节。您可以将字节累积到某种集合中,然后使用标准的Collection框架工具将其转换为数组供字符串构造函数使用。

当你看到它要切换(大概是某种插播信号,某些特定字节)的指标,然后切换到您的原始字节读取码。

+0

当我与自己的Java进程通信时,parsifal的答案起作用,但是当与外部程序(用C++编写)通信时,我不得不切换到这个方法 –

相关问题