0

System.inを介して外部ソースから情報を受信するプログラムがあります。入力モードには、ラインモードとrawモードの2つがあります。ラインモードでは、入力は単純に一連のUTF-8文字列であり、それぞれが改行文字で終了します。ラインモードのある時点で、Nバイトの生データを受信しようとしているという通知を受け取ります。その時点で、入力はrawモードに切り替わり、有効なUTF-8文字ではないrawバイナリデータを正確にNバイト受信します。この後、ラインモードに戻ります。

文字列の読み取りと生データの読み取りを簡単に切り替える方法はありますか?私の唯一の考えは、InputStreamをバイトごとに読み取り、文字に変換することです。System.inを複数のタイプの入力ストリームでラップする方法はありますか?2つの異なるラッパーから読み取ると問題が発生するように感じます。

(修正済み)更新:

parsifalの提案を試しましたが、問題が発生しています。入力モードの切り替えをシミュレートするために、テストハーネスを変更しました。(私が持っている別のプロセスも、最終的にはこの方法で出力する必要があることに気付きました。)問題の原因が送信側か受信側かはわかりません。出力モードを切り替えると、バイトが正しく読み込まれていないようです。また、表示されるのは常に同じバイト値です。以下にいくつかのコードの抜粋を示します。

修正: 問題は、明らかに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]

クライアント:
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]

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)
4

2 に答える 2

3

「utf-8」エンコーディングを指定する でラップSystem.inしてInputStreamReaderから、文字単位で読み取ることができます。文字を に蓄積し、StringBuilder必要に応じてディスパッチします (通常は が表示されたときです'\n'が、ビルダーのテストに基づく可能性があります)。

バイナリ データを読みたい場合は、基になるInputStream( System.in) から読み込むだけです。はInputStreamReader必要に応じて変換を実行し、データをバッファリングしません。

スタック内でバッファリングされたストリームやリーダーを使用したくない場合。readLine()これにより、少なくとも JDK クラスに限定されている場合は、メソッドを使用する機会がなくなります。


最新の更新に基づいて編集します。

生モードと調理済みモードの切り替えは少し疑わしいと思います。これを実装するとしたら、2 つのプリミティブ操作String readLine()byte[] readData(length). 1 つ目は改行までの文字を蓄積し、2 つ目は固定バッファーを読み取ります。次に、メインループは次のようになります。

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
}

また、ストリームの周りに構築されたオブジェクトにすべてをラップし、おそらくコールバックを使用してデータ パケットをディスパッチします。

于 2013-01-07T17:47:48.883 に答える
1

最善の策はSystem.in.read()、UTF-8 改行バイト 0x0A に到達するまでバイトごとに ( を使用して) バッファーに読み込み、そのバイト バッファーを文字列に変換することです ( を使用new String(byte[] bytes, "UTF-8"))。

InputStreamで呼び出されると、0 ~ 255 の値の int が返されることに注意してくださいread()。バイトにキャストする必要があります。ある種の Collection にバイトを蓄積してから、標準の Collection フレームワーク ツールを使用してそれを配列に変換し、String コンストラクターで使用できるようにすることができます。

切り替えが行われることを示すインジケーター (おそらくある種のインストリーム シグナリング、特定のバイト) が表示されたら、未加工のバイト読み取りコードに切り替えます。

于 2013-01-07T17:40:49.190 に答える