2009-02-06 22 views
17

¿Hay alguna manera de evitar que StreamReader realice ningún almacenamiento en búfer?StreamReader sin búfer

Estoy tratando de manejar la salida de un Proceso que puede ser binario o de texto. La salida se verá como una respuesta HTTP, p. Ej.

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

text or binary data here 

Lo que quiero hacer es analizar los encabezados utilizando un StreamReader, y después lee ya sea desde su BaseStream o la StreamReader para manejar el resto del contenido. Esto es básicamente lo que empecé con:

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

Esto parece a la basura de datos binarios. Así que cambié la última línea a algo como esto:

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

... pero StreamReader buffers en su entrada, por lo reader.BaseStream está en la posición incorrecta. ¿Hay alguna forma de desactivar el StreamReader? ¿O puedo decirle a StreamReader que restablezca la transmisión de regreso a donde está StreamReader?

+0

Matt - puede elaborar en "StreamReader lee bloques a la vez, por lo que reader.BaseStream está en la posición incorrecta". – jro

+0

espero que eso esté más claro. –

Respuesta

0

Bueno, puede usar Stream.Seek para establecer la posición de la secuencia. Me parece que el problema que tienes aquí es que StreamReader lee caracteres en lugar de bytes (que, dependiendo de la codificación, puede ser diferente de 1 byte por carácter). Desde el MSDN Library: StreamReader

está diseñado para la entrada de caracteres en una codificación particular, mientras que la clase Stream está diseñada para la entrada y salida de bytes.

Cuando llama a reader.ReadToEnd(), lee los datos en una cadena de caracteres en función de la codificación que está utilizando. Puede tener mejor suerte con el método Stream.Read. Lea sus datos de cadena con StreamReader y luego extraiga los datos binarios en un byte [] cuando haya leído en el encabezado que le notifica los datos binarios entrantes.

+0

Bueno, no puedes buscar dentro de NetworkStream. – nitrocaster

8

Esta respuesta es tardía y posiblemente ya no sea relevante para usted, pero puede ser útil para otra persona que tropieza con este problema.

Mi problema implicado PPM files, que tienen un formato similar de:

  • texto ASCII en el principio
  • bytes binarios para el resto del archivo

El problema que encontramos fue que la clase StreamReader es incapaz de leer cosas de un byte a la vez sin almacenar en el búfer. Esto causó resultados inesperados en algunos casos, ya que el método Read() lee un solo carácter, no un solo byte.

Mi solución fue escribir un contenedor alrededor de una secuencia que leería los bytes uno a la vez. El contenedor tiene 2 métodos importantes, ReadLine() y Read().

Estos 2 métodos me permiten leer las líneas ASCII de una secuencia, sin búfer, y luego leer un solo byte a la vez para el resto de la secuencia. Es posible que deba hacer algunos ajustes para satisfacer sus necesidades.

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

Para mejorar su solución, debe devolver nulo si la lista de bytes está vacía para cumplir con la definición de la clase base TextReader. Actualmente estás devolviendo una cadena vacía. – Doomjunky

Cuestiones relacionadas