2009-02-06 7 views
17

Czy istnieje sposób, aby StreamReader nie buforował?Niepobudzony StreamReader

Próbuję obsłużyć dane wyjściowe z procesu, który może być binarny lub tekstowy. Dane wyjściowe będą wyglądały jak odpowiedź HTTP, np.

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

text or binary data here 

Co chcę zrobić jest do analizowania nagłówków używając StreamReader, a następnie albo czytać z jego BaseStream lub StreamReader obsłużyć resztę zawartości. Oto w zasadzie to, co zacząłem:

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

To wydaje się wyrzucać dane binarne. Więc zmieniłem ostatni wiersz do czegoś takiego:

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

... ale StreamReader buforuje dane wejściowe, więc reader.BaseStream znajduje się w niewłaściwej pozycji. Czy istnieje sposób na zdekoncentrowanie StreamReadera? Czy mogę powiedzieć StreamReader, aby zresetować strumień z powrotem do miejsca, w którym znajduje się StreamReader?

+0

Matt - możesz rozwinąć na "StreamReader czyta bloki na raz, więc czytelnik.BaseStream jest w złym położeniu." – jro

+0

mam nadzieję, że to będzie bardziej przejrzyste. –

Odpowiedz

0

Można użyć pozycji Stream.Seek, aby ustawić pozycję strumienia. Wydaje mi się, że problem, który tu masz, polega na tym, że StreamReader czyta raczej znaki niż bajty (które, zależnie od kodowania, mogą być inne niż 1 bajt na znak). Od MSDN Library:

StreamReader przeznaczony jest do postaci wejścia w danym kodowaniu, natomiast klasy Stream został zaprojektowany dla wejścia i wyjścia bajtów.

Podczas wywoływania read.ReadToEnd() odczytuje dane jako łańcuch znaków w oparciu o kodowanie, z którego korzysta. Możesz mieć więcej szczęścia używając metody Stream.Read. Czytaj w swoich ciągach danych za pomocą StreamReadera, a następnie wyciągnij dane binarne do bajtu [] po przeczytaniu w nagłówku, który informuje cię o przychodzących danych binarnych.

+0

Nie można wyszukiwać w środowisku NetworkStream. – nitrocaster

8

Ta odpowiedź jest spóźniona i prawdopodobnie nie jest już dla ciebie istotna, ale może być przydatna dla kogoś, kto natknął się na ten problem.

Mój problem zaangażowany PPM files, które mają podobny format:

  • ASCII tekstu na początku
  • binarne bajty dla pozostałej części pliku

Problem wpadłem było klasa StreamReader nie jest w stanie czytać jednego bajtu na raz bez buforowania. Spowodowało to nieoczekiwane wyniki w niektórych przypadkach, ponieważ metoda Read() czyta pojedynczy znak, a nie pojedynczy bajt.

Moim rozwiązaniem było napisanie wrappera wokół strumienia, który odczytywałby bajty po jednym na raz. Opakowanie ma 2 ważne metody, ReadLine() i .

Te dwie metody pozwalają mi czytać linie ASCII strumienia, niebuforowane, a następnie odczytywać pojedynczy bajt w czasie dla reszty strumienia. Być może będziesz musiał dokonać pewnych korekt, dopasowanych do swoich potrzeb.

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

Aby poprawić swoje rozwiązanie, musisz zwrócić wartość null, jeśli lista bajtów jest pusta, aby spełnić definicję klasy podstawowej TextReader. Obecnie zwracasz pusty ciąg znaków. – Doomjunky

Powiązane problemy