Чтение строки из потокового ридера без потребления? - PullRequest
24 голосов
/ 09 мая 2009

Есть ли способ прочитать вперед на одну строку, чтобы проверить, содержит ли следующая строка определенные данные тега?

Я имею дело с форматом, в котором есть начальный тег, но нет конечного тега.

Я хотел бы прочитать строку, добавить ее в структуру, затем проверить строку ниже, чтобы убедиться, что это не новый "узел", и если он не будет продолжать добавление, если он близок к этой структуре, и создать новую

единственное решение, которое я могу придумать, - это чтобы два потоковых считывателя двигались в одно и то же время, что-то вроде шага вдоль шага блокировки, но это кажется бесполезным (если оно даже будет работать)

мне нужно что-то вроде взгляда, но peekline

Ответы [ 4 ]

29 голосов
/ 09 мая 2009

Проблема в том, что основной поток может даже не быть доступным для поиска. Если вы посмотрите на реализацию программы чтения потоков, она использует буфер, чтобы реализовать TextReader.Peek (), даже если поток недоступен для поиска.

Вы можете написать простой адаптер, который читает следующую строку и внутренне буферизует, что-то вроде этого:

 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();
        }
    }
4 голосов
/ 09 мая 2009

Вы можете сохранить позицию, обращающуюся к StreamReader.BaseStream.Position, затем прочитать строку на следующей строке, выполнить тест, а затем выполнить поиск позиции перед тем, как прочитать строку:

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

Это много поиска и перечитывания одних и тех же строк. Используя некоторую логику, вы можете вообще избежать этого - например, когда вы видите начало нового тега, закрытие существующей структуры и запуск новой - вот основной алгоритм:

        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 голос
/ 09 мая 2009

Почему сложность? Верните следующую строку, независимо. Проверьте, является ли это новый узел, если нет, добавьте его в структуру. Если это так, создайте новую структуру.

// 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 голосов
/ 09 мая 2009

Вот что я делаю до сих пор. Я прошел больше разделенного маршрута, чем потоковый читатель, маршрут за строкой.

Я уверен, что есть несколько мест, которые умирают, чтобы быть более элегантными, но сейчас это, кажется, работает.

Пожалуйста, дайте мне знать, что вы думаете

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