2009-05-09 10 views
22

C'è un modo per leggere in anticipo una riga per verificare se la riga successiva contiene dati di tag specifici?Leggere una riga da un streamreader senza consumare?

Ho a che fare con un formato che ha un tag di inizio ma nessun tag di chiusura.

Vorrei leggere una riga aggiungerla a una struttura, quindi testare la riga seguente per assicurarsi che non sia un nuovo "nodo" e se non viene aggiunta aggiungendo se è chiusa da quella struttura e creare una nuova uno

l'unica soluzione che posso pensare è quello di avere due lettori di flusso che vanno allo stesso tempo un pò suffling c'è strada lungo passo di blocco ma che sembra wastefull (se sarà anche il lavoro)

ho bisogno di qualcosa di simile a peek ma Peekline

+0

Penso che l'approccio PeekLine non è un buon modo per affrontare "senza tag di chiusura" problema, perché si ha sempre di sbirciare linea e prova wherher nuova struttura ha inizio. Mi piacerebbe impostare la posizione del flusso sulla riga precedente e il prossimo ReadLine restituirà la linea che hai letto. – Gqqnbig

risposta

26

Il problema è il flusso sottostante potrebbe non essere nemmeno ricercabile. Se date un'occhiata all'implementazione dello stream reader, utilizza un buffer in modo che possa implementare TextReader.Peek() anche se lo stream non è ricercabile.

si potrebbe scrivere un semplice adattatore che legge la riga successiva e buffer internamente, qualcosa di simile:

public class PeekableStreamReaderAdapter 
    { 
     private StreamReader Underlying; 
     private Queue<string> BufferedLines; 

     public PeekableStreamReaderAdapter(StreamReader underlying) 
     { 
      Underlying = underlying; 
      BufferedLines = new Queue<string>(); 
     } 

     public string PeekLine() 
     { 
      string line = Underlying.ReadLine(); 
      if (line == null) 
       return null; 
      BufferedLines.Enqueue(line); 
      return line; 
     } 


     public string ReadLine() 
     { 
      if (BufferedLines.Count > 0) 
       return BufferedLines.Dequeue(); 
      return Underlying.ReadLine(); 
     } 
    } 
+2

Inizializzerei il 'BufferedLines' prima dell'uso :) e inoltre, userei un altro nome per PeekLine(), poiché il nome suggerisce che restituirebbe sempre la stessa riga (la riga successiva dalla posizione dell'ultima ReadLine). Votato +1 già – tofi9

+1

Grazie aggiunto l'inizializzatore. Mai nemmeno compilato il codice. Forse qualcosa come LookAheadReadLine() potrebbe essere più appropriato. –

+7

L'ho espanso leggermente in modo che la classe erediti da TextReader: https: //gist.github.com/1317325 –

4

È possibile memorizzare la posizione che accede a StreamReader.BaseStream.Position, quindi leggere la riga successiva, eseguire il test , Poi cercare di posizione prima di leggere la linea:

  // Peek at the next line 
      long peekPos = reader.BaseStream.Position; 
      string line = reader.ReadLine(); 

      if (line.StartsWith("<tag start>")) 
      { 
       // This is a new tag, so we reset the position 
       reader.BaseStream.Seek(pos);  

      } 
      else 
      { 
       // This is part of the same node. 
      } 

Questo è un sacco di cercare e rileggendo le stesse linee. Usando un po 'di logica, si può essere in grado di evitare questo del tutto - per esempio, quando si vede un nuovo inizio tag, chiudere la struttura esistente e iniziare uno nuovo - qui è un algoritmo di base:

 SomeStructure myStructure = null; 
     while (!reader.EndOfStream) 
     { 
      string currentLine = reader.ReadLine(); 
      if (currentLine.StartsWith("<tag start>")) 
      { 
       // Close out existing structure. 
       if (myStructure != null) 
       { 
        // Close out the existing structure. 
       } 

       // Create a new structure and add this line. 
       myStructure = new Structure();     
       // Append to myStructure. 
      } 
      else 
      { 
       // Add to the existing structure. 
       if (myStructure != null) 
       { 
        // Append to existing myStructure 
       } 
       else 
       { 
        // This means the first line was not part of a structure. 
        // Either handle this case, or throw an exception. 
       } 
      } 
     } 
+1

Guardando qui: sembra che la posizione del flusso sottostante potrebbe non corrispondere sempre a quello di StreamReader: http: //stackoverflow.com/questions/1737591/streamreader-c-peek – Casebash

1

Perché la difficoltà? Restituisce la riga successiva, indipendentemente. Controlla se si tratta di un nuovo nodo, in caso contrario, aggiungilo alla struct. Se lo è, crea una nuova struttura.

// Not exactly C# but close enough 
Collection structs = new Collection(); 
Struct struct; 
while ((line = readline()) != null)) { 
    if (IsNode(line)) { 
     if (struct != null) structs.add(struct); 
     struct = new Struct(); 
     continue; 
    } 
    // Whatever processing you need to do 
    struct.addLine(line); 
} 
structs.add(struct); // Add the last one to the collection 

// Use your structures here 
foreach s in structs { 

} 
0

Ecco quello che vado così lontano. Sono andato più della rotta divisa rispetto allo streamreader linea per linea.

Sono sicuro che ci sono alcuni posti che stanno morendo per essere più eleganti ma per ora sembra funzionare.

Per favore fatemi sapere cosa ne pensate

struct INDI 
    { 
     public string ID; 
     public string Name; 
     public string Sex; 
     public string BirthDay; 
     public bool Dead; 


    } 
    struct FAM 
    { 
     public string FamID; 
     public string type; 
     public string IndiID; 
    } 
    List<INDI> Individuals = new List<INDI>(); 
    List<FAM> Family = new List<FAM>(); 
    private void button1_Click(object sender, EventArgs e) 
    { 
     string path = @"C:\mostrecent.ged"; 
     ParseGedcom(path); 
    } 

    private void ParseGedcom(string path) 
    { 
     //Open path to GED file 
     StreamReader SR = new StreamReader(path); 

     //Read entire block and then plit on 0 @ for individuals and familys (no other info is needed for this instance) 
     string[] Holder = SR.ReadToEnd().Replace("0 @", "\u0646").Split('\u0646'); 

     //For each new cell in the holder array look for Individuals and familys 
     foreach (string Node in Holder) 
     { 

      //Sub Split the string on the returns to get a true block of info 
      string[] SubNode = Node.Replace("\r\n", "\r").Split('\r'); 
      //If a individual is found 
      if (SubNode[0].Contains("INDI")) 
      { 
       //Create new Structure 
       INDI I = new INDI(); 
       //Add the ID number and remove extra formating 
       I.ID = SubNode[0].Replace("@", "").Replace(" INDI", "").Trim(); 
       //Find the name remove extra formating for last name 
       I.Name = SubNode[FindIndexinArray(SubNode, "NAME")].Replace("1 NAME", "").Replace("/", "").Trim(); 
       //Find Sex and remove extra formating 
       I.Sex = SubNode[FindIndexinArray(SubNode, "SEX")].Replace("1 SEX ", "").Trim(); 

       //Deterine if there is a brithday -1 means no 
       if (FindIndexinArray(SubNode, "1 BIRT ") != -1) 
       { 
        // add birthday to Struct 
        I.BirthDay = SubNode[FindIndexinArray(SubNode, "1 BIRT ") + 1].Replace("2 DATE ", "").Trim(); 
       } 

       // deterimin if there is a death tag will return -1 if not found 
       if (FindIndexinArray(SubNode, "1 DEAT ") != -1) 
       { 
        //convert Y or N to true or false (defaults to False so no need to change unless Y is found. 
        if (SubNode[FindIndexinArray(SubNode, "1 DEAT ")].Replace("1 DEAT ", "").Trim() == "Y") 
        { 
         //set death 
         I.Dead = true; 
        } 
       } 
       //add the Struct to the list for later use 
       Individuals.Add(I); 
      } 

      // Start Family section 
      else if (SubNode[0].Contains("FAM")) 
      { 
       //grab Fam id from node early on to keep from doing it over and over 
       string FamID = SubNode[0].Replace("@ FAM", ""); 

       // Multiple children can exist for each family so this section had to be a bit more dynaimic 

       // Look at each line of node 
       foreach (string Line in SubNode) 
       { 
        // If node is HUSB 
        if (Line.Contains("1 HUSB ")) 
        { 

         FAM F = new FAM(); 
         F.FamID = FamID; 
         F.type = "PAR"; 
         F.IndiID = Line.Replace("1 HUSB ", "").Replace("@","").Trim(); 
         Family.Add(F); 
        } 
         //If node for Wife 
        else if (Line.Contains("1 WIFE ")) 
        { 
         FAM F = new FAM(); 
         F.FamID = FamID; 
         F.type = "PAR"; 
         F.IndiID = Line.Replace("1 WIFE ", "").Replace("@", "").Trim(); 
         Family.Add(F); 
        } 
         //if node for multi children 
        else if (Line.Contains("1 CHIL ")) 
        { 
         FAM F = new FAM(); 
         F.FamID = FamID; 
         F.type = "CHIL"; 
         F.IndiID = Line.Replace("1 CHIL ", "").Replace("@", ""); 
         Family.Add(F); 
        } 
       } 
      } 
     } 
    } 

    private int FindIndexinArray(string[] Arr, string search) 
    { 
     int Val = -1; 
     for (int i = 0; i < Arr.Length; i++) 
     { 
      if (Arr[i].Contains(search)) 
      { 
       Val = i; 
      } 
     } 
     return Val; 
    } 
+1

FAM e INDI sono nomi orribili per quelle strutture (se qualcun altro potrebbe aver bisogno di leggere o lavorare con il tuo codice). –

+0

Questo è il nome del tag Ho pensato che fosse abbastanza esplicativo – Crash893

Problemi correlati