2009-05-09 18 views
22

Existe-t-il un moyen de lire en amont une ligne pour tester si la ligne suivante contient des données d'étiquette spécifiques?Lire une ligne à partir d'un lecteur de flux sans consommer?

Je parle d'un format qui a une balise de début mais pas de balise de fin. Je voudrais lire une ligne l'ajouter à une structure puis tester la ligne ci-dessous pour m'assurer qu'il ne s'agit pas d'un nouveau "noeud" et si ce n'est pas continuer à ajouter s'il est proche de cette structure et faire un nouveau un

la seule solution que je peux penser est d'avoir deux lecteurs de flux en cours en même temps Suffling un peu là chemin le long de l'étape de verrouillage, mais qui semble wastefull (si elle fonctionnera même)

je besoin de quelque chose comme coup d'oeil mais peekline

+0

Je pense que l'approche PeekLine est pas une bonne façon de traiter « pas de balise fermante » problème, parce que vous avez toujours de jeter un regard en ligne et tester wherher nouvelle structure commence. Je voudrais définir la position du flux sur la ligne précédente et ReadLine suivante retournera la ligne que vous avez lue. – Gqqnbig

Répondre

26

Le problème est le flux sous-jacent peut même ne pas être recherché. Si vous jetez un oeil à l'implémentation du lecteur de flux, il utilise un tampon afin qu'il puisse implémenter TextReader.Peek() même si le flux n'est pas accessible.

Vous pouvez écrire un simple adaptateur qui lit la ligne suivante et des tampons en interne, quelque chose comme ceci:

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

Je voudrais initialiser le 'BufferedLines' avant utilisation :) et aussi, j'utiliserais un autre nom pour PeekLine(), car le nom suggère qu'il retournerait toujours la même ligne (la ligne suivante de la position de la dernière ReadLine). Voté +1 déjà – tofi9

+1

Merci ajouté l'initialiseur. Jamais même compilé le code. Peut-être que quelque chose comme LookAheadReadLine() pourrait être plus approprié. –

+7

Je l'ai développé légèrement afin que la classe hérite de TextReader: https: //gist.github.com/1317325 –

4

Vous pouvez stocker la position d'accès StreamReader.BaseStream.Position, puis lire la ligne suivante, faites votre test , Puis chercher à la position avant de lire la ligne:

  // 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. 
      } 

Ceci est beaucoup de recherche et relisant les mêmes lignes. En utilisant une certaine logique, vous pourrez peut-être éviter ce tout à fait - par exemple, quand vous voyez un nouveau départ de l'étiquette, fermez la structure existante et commencer une nouvelle - voici un algorithme de 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

Regarde ici: il semble que la position du flux sous-jacent ne correspond pas toujours à ce StreamReader: http: //stackoverflow.com/questions/1737591/streamreader-c-peek – Casebash

1

Pourquoi la difficulté? Retourne la ligne suivante, peu importe. Vérifiez s'il s'agit d'un nouveau noeud, sinon, ajoutez-le à la structure. Si c'est le cas, créez une nouvelle structure.

// 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

Voici ce que je vais jusqu'à présent. Je suis allé plus de la route fractionnée que le lecteur ligne par ligne.

Je suis sûr qu'il y a quelques endroits qui meurent pour être plus élégant mais pour le moment il semble fonctionner.

S'il vous plaît laissez-moi savoir ce que vous pensez

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 et INDI sont des noms horribles pour ces structures (si quelqu'un d'autre peut avoir besoin de lire ou de travailler avec votre code). –

+0

C'est le nom de la balise que j'ai trouvé assez explicite – Crash893