Небуферизованный StreamReader - PullRequest
17 голосов
/ 06 февраля 2009

Есть ли способ не дать StreamReader выполнять буферизацию?

Я пытаюсь обработать вывод из процесса, который может быть двоичным или текстовым. Вывод будет выглядеть как 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?

Ответы [ 2 ]

8 голосов
/ 23 марта 2010

Этот ответ запоздал и, возможно, больше не относится к вам, но может пригодиться кому-то еще, кто сталкивается с этой проблемой.

Моя проблема связана с файлами PPM , которые имеют аналогичный формат:

  • ASCII текст в начале
  • Двоичные байты для остальной части файла

Проблема, с которой я столкнулся, заключалась в том, что класс StreamReader не способен читать данные по одному байту за раз без буферизации материала. В некоторых случаях это приводило к неожиданным результатам, поскольку метод Read() читает один символ, а не один байт.

Мое решение было написать обертку вокруг потока, которая будет читать байты по одному. Обертка имеет 2 важных метода, ReadLine() и Read().

Эти 2 метода позволяют мне читать строки ASCII потока без буферизации, а затем читать по одному байту за раз для остальной части потока. Возможно, вам придется внести некоторые изменения в соответствии с вашими потребностями.

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();
    }
}
0 голосов
/ 06 февраля 2009

Ну, вы можете использовать Stream.Seek , чтобы установить позицию потока. Мне кажется, что проблема, с которой вы здесь столкнулись, заключается в том, что StreamReader читает символы, а не байты (которые, в зависимости от кодировки, могут отличаться от 1 байта на символ). Из библиотеки MSDN :

StreamReader предназначен для персонажа ввод в конкретной кодировке, тогда как класс Stream разработан для байтового ввода и вывода.

Когда вы вызываете reader.ReadToEnd (), он считывает данные в виде строки символов в зависимости от используемой им кодировки. Возможно, вам повезет больше, если использовать метод Stream.Read . Считайте ваши строковые данные с помощью StreamReader, а затем извлеките двоичные данные в байт [], когда прочитаете заголовок, который уведомляет вас о поступающих двоичных данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...