バイト配列から行を読み取ります (バイト配列を文字列に変換しません)

StackOverflow https://stackoverflow.com/questions/492454

質問

NetworkStream から読み取っているバイト配列があります。最初の 2 バイトは後続のパケットの長さを示し、パケットはその長さのバイト配列に読み込まれます。NetworkStream/byte 配列から読み取る必要があるデータには、いくつかの文字列が含まれています。改行文字で終了する可変長データと、バイトやロングなどのいくつかの固定幅フィールド。したがって、次のようになります。

// I would have delimited these for clarity but I didn't want
// to imply that the stream was delimited because it's not.
StringbyteStringStringbytebytebytelonglongbytelonglong

私は、受信するデータ パケットの形式を知っています (そして、ある程度の発言権も持っています)。私が行う必要があるのは、文字列値ごとに「行」を読み取ることですが、バイトとロングについては固定バイト数を読み取ることです。これまでのところ、私が提案する解決策は、 while ループを実行して、改行文字が存在するまで一時バイト配列にバイトを読み取ります。次に、バイトを文字列に変換します。これは私にはわかりにくいように思えますが、他に明白な方法が見つかりません。使えることに気づきました StreamReader.ReadLine() しかし、それには別のストリームが関係するため、すでに NetworkStream. 。しかし、それがより良い解決策である場合は、試してみます。

私が検討したもう 1 つのオプションは、バックエンド チームにこれらの文字列値の長さを 1 ~ 2 バイト書き込ませることで、長さを読み取り、指定された長さに基づいて文字列を読み取ることができるようにすることです。

ご覧のとおり、これに取り組む方法についてはいくつかの選択肢があります。最善の方法は何だと思われるかについて、ご意見をいただきたいと思います。これは、パケット全体を文字列として読み取るための現時点でのコードです。次のステップでは、パケットのさまざまなフィールドを分割し、オブジェクトの作成、UI の更新など、実行する必要がある実際のプログラミング作業を実行します。パケット内のデータに基づいて。

string line = null;  
while (stream.DataAvailable)
{  
    //Get the packet length;  
    UInt16 packetLength = 0;  
    header = new byte[2];  
    stream.Read(header, 0, 2);  
    // Need to reverse the header array for BitConverter class if architecture is little endian.  
    if (BitConverter.IsLittleEndian)
        Array.Reverse(header);  
    packetLength = BitConverter.ToUInt16(header,0);

    buffer = new byte[packetLength];
    stream.Read(buffer, 0, BitConverter.ToUInt16(header, 0));
    line = System.Text.ASCIIEncoding.ASCII.GetString(buffer);
    Console.WriteLine(line);
}
役に立ちましたか?

解決

個人的にはそうするだろう

  1. 文字列の開始時にINT16を置いてください。
  2. io.binaryreaderクラスを使用して読み取りを行い、「読み」、ints、文字列、charなどに変数になります。BinReader.ReadInt16() は 2 バイトを読み取り、それらが表す int16 を返し、ストリーム内で 2 バイトを移動します。

お役に立てれば。

追伸ReadString メソッドの使用には注意してください。文字列の前にカスタム 7 ビット整数が付加されていることが前提となっています。BinaryWriter クラスによって書き込まれたことを示します。以下はこちらから コードグル 役職

BinaryWriter クラスには 2 つのメソッドがあります 文字列を書き込む場合:オーバーロードされた Write() メソッドと WriteString() 方式。前者は文字列 に従ってバイトのストリームとして クラスが使用しているエンコード。ザ WriteString() メソッドでは、 指定されたエンコーディングですが、接頭辞 文字列のバイトストリームを 文字列の実際の長さ。そういう 接頭辞付きの文字列は、 BinaryReader.ReadString()を呼び出します。

長さの面白いところ できるだけ少ないバイト数にしてください このサイズを保持するために使用されます。 7 ビットと呼ばれる型として格納されます エンコードされた整数。長さが収まる場合 7 ビット 1 バイトが使用されます ( これより大きい場合は、上位ビットがオンになります 最初のバイトが設定され、2 番目のバイトが byteは、値をシフトすることによって作成されます 7 ビット単位で。これを 連続するバイトが 値を保持するのに十分なバイト数。これ メカニズムは、 長さは 取られたサイズのかなりの部分 直列化された文字列で上に移動します。BinaryWriter と BinaryReader には 7ビットを読み書きする方法 エンコードされた整数ですが、 保護されているため、それらのみを使用できます これらのクラスから派生する場合。

他のヒント

私なら、長さの接頭辞が付いた文字列を使用します。これにより作業が大幅に簡素化され、改行を入れて文字列を表現できることになります。ただし、コードに関するいくつかのコメント:

  • Stream.DataAvailable は使用しないでください。データが無いからといって ストリームの最後まで読んだという意味ではありません。
  • ASCII を超えるテキストが絶対に必要ないという確信がない限り、ASCIIEncoding を使用しないでください。
  • Stream.Read が要求されたすべてのデータを読み取るとは想定しないでください。 いつも 戻り値を確認してください。
  • BinaryReader を使用すると、これの多くがはるかに簡単になります (長さのプレフィックス付き文字列や、要求された内容を読み取るまでループする Read など)。
  • 同じデータに対して BitConverter.ToUInt16 を 2 回呼び出しています。なぜ?
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top