Filtraggio di flussi in C#
Domanda
Qual è il modo più semplice per filtrare un flusso/lettore riga per linea in C# (un po 'come mettere SED al centro di una pipeline). Voglio alimentare un file ICALENDAR a DDay.ical ma Dday.ical Dies in "Versione: 5.1.1" perché vuole un numero o un numero di semili (dove il numero è cifre (cifre dot)? Quindi l'ultima ". inaspettato).
Quello che voglio fare è filtrare la versione: riga a qualcosa di innocuo come "Versione: 5.1" in modo che il parser non muoia.
AGGIORNAMENTO: OK, ecco un esempio:
BEGIN:VCALENDAR
PRODID:-//SunONE/Calendar Hosting Server//EN
METHOD:PUBLISH
VERSION:5.1.1
X-NSCP-CALPROPS-LAST-MODIFIED:20011208T005613Z
X-NSCP-CALPROPS-CREATED:20010913T223336Z
X-NSCP-CALPROPS-READ:999
X-NSCP-CALPROPS-WRITE:999
Ora, il parser Dday.ical non piace "Versione: 5.1.1", quindi voglio sostituirlo con qualcosa di innocuo come "Versione: 5.1".
L'interfaccia parser prende un lettore o un flusso.
Comunque, ho provato a usare il codice qui e funziona (reimplementazione di Textreader in cima a una readline filtrata).
Soluzione
System.io.stream utilizza il motivo del decoratore in modo che sia abbastanza facile crearne uno che avvolge un flusso sottostante. Ciò consente a flussi come Cryptostream e Gzipstream di avvolgere qualsiasi altra istanza del flusso e "di sopravvivere" i suoi metodi di lettura/scrittura senza derivare dalla classe che si desidera estendere. Modello di design molto flessibile e popolare descritto nella banda di quattro libri.
Ora non sono sicuro che l'API con cui stai lavorando richieda un flusso o un flusso. C'è una distinzione significativa tra i due. Un flusso di flusso funziona al testo Livello e operazioni su caratteri/linee. Un flusso funziona a livello binario e opera su byte. In altre parole, si prevede che il REAder Stream sia in grado di decodificare i byte nel testo in modo che il consumatore non debba preoccuparsi della codifica. Utilizzare un flusso quando la codifica non ha importanza (ad esempio quando si comprime o entusiasmo) e usa un READER Stream quando si lavora con i dati di testo.
Da quello che sembra, un READER Stream avrebbe più senso qui. Se l'API può accettare un READER, basta derivare il proprio da Textreader e sovrascrivere il suo metodo di lettura in modo che la prima chiamata restituisca la riga di testo necessaria e le chiamate successive funzionino solo come normali.
L'altra opzione è quella di utilizzare solo un stringwriter/stringreder e riempi tutto in un buffer di stringa in memoria, manipolalo, quindi passalo.
Altri suggerimenti
Il modo più semplice potrebbe essere quello di avvolgere il flusso come un ignibo e filtrare con Linq:
static void Main(string[] args)
{
System.IO.StreamReader sr = // ...
var filtered = Enumerable.Where(
StreamReaderToSeq(sr), input => { int temp; return int.TryParse(x, out temp); });
}
static IEnumerable<string> StreamReaderToSeq(System.IO.StreamReader sr)
{
while(!sr.EndOfStream)
{
yield return sr.ReadLine();
}
}
La sequenza sopra filtri solo numeri interi, ma è abbastanza facile scrivere un filtro migliore per gestire tutti gli ingressi desiderati.