2009-02-06 6 views
17

C'è un modo per impedire a StreamReader di eseguire il buffering?Streamer Unbuffered

Sto provando a gestire l'output da un processo che può essere sia binario che testo. L'output sarà simile a una risposta HTTP, ad es.

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

text or binary data here 

Quello che voglio fare è quello di analizzare le intestazioni usando un StreamReader, e poi o leggere dalla sua BaseStream o il StreamReader per gestire il resto del contenuto. Ecco in sostanza cosa ho iniziato 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()); 
} 

Questo sembra cancellare dati binari. Così ho cambiato l'ultima riga a qualcosa di simile:

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

... ma StreamReader il buffer per il suo ingresso, in modo da reader.BaseStream è nella posizione sbagliata. C'è un modo per smistare StreamReader? O posso dire a StreamReader di reimpostare lo stream su dove si trova StreamReader?

+0

Matt: puoi approfondire "StreamReader legge blocchi alla volta, quindi reader.BaseStream è nella posizione sbagliata." – jro

+0

speriamo che sia più chiaro. –

risposta

0

Bene, è possibile utilizzare Stream.Seek per impostare la posizione del flusso. Mi sembra che il problema che stai avendo qui è che StreamReader sta leggendo i caratteri piuttosto che i byte (che, a seconda della codifica, possono essere diversi da 1 byte per carattere). Dalla MSDN Library: StreamReader

è progettato per carattere ingresso in una codifica particolare, mentre la classe Stream è progettato per l'ingresso e l'uscita di byte.

Quando si chiama reader.ReadToEnd(), legge i dati come una stringa di caratteri basata su qualsiasi codifica che sta utilizzando. Potresti avere più fortuna con il metodo Stream.Read. Leggi i tuoi dati di stringa con StreamReader e poi estrai i dati binari in un byte [] quando hai letto nell'intestazione che ti informa dei dati binari in entrata.

+0

Bene, non è possibile cercare all'interno di NetworkStream. – nitrocaster

8

Questa risposta è in ritardo e probabilmente non è più pertinente per te, ma potrebbe rivelarsi utile per qualcun altro che si imbatte in questo problema.

Il mio problema coinvolto PPM files, che hanno un formato simile di:

  • testo ASCII in principio
  • byte binari per il resto del file

Il problema che ho avuto era che la classe StreamReader non sia in grado di leggere roba un byte alla volta senza bufferizzare. Ciò ha causato risultati imprevisti in alcuni casi, poiché il metodo Read() legge un singolo carattere, non un singolo byte.

La mia soluzione era scrivere un wrapper attorno a un flusso che leggesse i byte uno alla volta. Il wrapper ha 2 metodi importanti, ReadLine() e Read().

Questi 2 metodi consentono di leggere le righe ASCII di un flusso, non bufferizzate e quindi di leggere un singolo byte alla volta per il resto del flusso. Potrebbe essere necessario apportare alcune modifiche in base alle proprie esigenze.

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

Per migliorare la soluzione è necessario restituire null se l'elenco di byte è vuoto per soddisfare la definizione della classe di base TextReader. Attualmente stai restituendo una stringa vuota. – Doomjunky

Problemi correlati