21

StreamReader がバッファリングを行わないようにする方法はありますか?

バイナリまたはテキストの可能性がある Process からの出力を処理しようとしています。出力は HTTP レスポンスのようになります。

Content-type: application/whatever
Another-header: value

text or binary data here

私がやりたいことは、 を使用してヘッダーを解析し、StreamReaderそのBaseStreamかから読み取りStreamReader、残りのコンテンツを処理することです。基本的に私が始めたのは次のとおりです。

private static readonly Regex HttpHeader = new Regex("([^:]+): *(.*)");
private void HandleOutput(StreamReader reader)
{
  var headers = new NameValueCollection();
  string line;
  while((line = reader.ReadLine()) != null)
  {
    Match header = HttpHeader.Match(line);
    if(header.Success)
    {
      headers.Add(header.Groups[1].Value, header.Groups[2].Value);
    }
    else
    {
      break;
    }
  }
  DoStuff(reader.ReadToEnd());
}

これはバイナリデータを破壊するようです。そこで、最後の行を次のように変更しました。

if(headers["Content-type"] != "text/html")
{
  // reader.BaseStream.Position is not at the same place that reader
  // makes it looks like it is.
  // i.e. reader.Read() != reader.BaseStream.Read()
  DoBinaryStuff(reader.BaseStream);
}
else
{
  DoTextStuff(reader.ReadToEnd());
}

...しかし、StreamReader はその入力をバッファリングするため、reader.BaseStream は間違った位置にあります。StreamReader のバッファを解除する方法はありますか? または、StreamReader にストリームをリセットして StreamReader がある場所に戻すように指示できますか?

4

2 に答える 2

9

この回答は遅れており、おそらくあなたには関係ありませんが、この問題に出くわした他の誰かにとっては役立つかもしれません.

私の問題は、次のような形式のPPM ファイルに関係していました。

  • 先頭の ASCII テキスト
  • ファイルの残りのバイナリ バイト

私が遭遇した問題は、StreamReaderクラスがバッファリングせずに一度に 1 バイトずつ読み取ることができないことでした。Read()このメソッドは 1 バイトではなく 1 文字を読み取るため、場合によっては予期しない結果が発生していました。

私の解決策は、一度に 1 バイトずつ読み取るストリームのラッパーを作成することでした。ラッパーには 2 つの重要なメソッドがReadLine()ありRead()ます。

これらの 2 つのメソッドを使用すると、ストリームの ASCII 行をバッファーなしで読み取ってから、ストリームの残りの部分を一度に 1 バイトずつ読み取ることができます。必要に応じて、いくつかの調整が必要になる場合があります。

class UnbufferedStreamReader: TextReader
{
    Stream s;

    public UnbufferedStreamReader(string path)
    {
        s = new FileStream(path, FileMode.Open);
    }

    public UnbufferedStreamReader(Stream stream)
    {
        s = stream;
    }

    // This method assumes lines end with a line feed.
    // You may need to modify this method if your stream
    // follows the Windows convention of \r\n or some other 
    // convention that isn't just \n
    public override string ReadLine()
    {
        List<byte> bytes = new List<byte>();
        int current;
        while ((current = Read()) != -1 && current != (int)'\n')
        {
            byte b = (byte)current;
            bytes.Add(b);
        }
        return Encoding.ASCII.GetString(bytes.ToArray());
    }

    // Read works differently than the `Read()` method of a 
    // TextReader. It reads the next BYTE rather than the next character
    public override int Read()
    {
        return s.ReadByte();
    }

    public override void Close()
    {
        s.Close();
    }
    protected override void Dispose(bool disposing)
    {
        s.Dispose();
    }

    public override int Peek()
    {
        throw new NotImplementedException();
    }

    public override int Read(char[] buffer, int index, int count)
    {
        throw new NotImplementedException();
    }

    public override int ReadBlock(char[] buffer, int index, int count)
    {
        throw new NotImplementedException();
    }       

    public override string ReadToEnd()
    {
        throw new NotImplementedException();
    }
}
于 2010-03-23T00:30:17.477 に答える
0

さて、Stream.Seekを使用してストリームの位置を設定できます。ここで発生している問題は、StreamReader がバイトではなく文字を読み取っていることです (エンコーディングによっては、1 文字あたり 1 バイトとは異なる場合があります)。MSDN ライブラリから:

StreamReader は特定のエンコーディングでの文字入力用に設計されていますが、Stream クラスはバイトの入出力用に設計されています。

reader.ReadToEnd() を呼び出すと、使用しているエンコーディングに基づいて、データが文字列として読み込まれます。Stream.Readメソッドを使用すると、うまくいくかもしれません。StreamReader を使用して文字列データを読み取り、受信バイナリ データを通知するヘッダーを読み取ったら、バイナリ データを byte[] に取り出します。

于 2009-02-06T17:21:05.247 に答える