2014-10-19 4 views
0

Я пишу парсер файлов в приложении .NET, который читает файл с помощью StreamReader. Файл, подлежащий анализу, начинается с заголовка, который заканчивается "<eoh>". Я хочу либо читать, либо игнорировать все с самого начала до этой строки. После этого начнутся фактические данные.Чтение с помощью StreamReader до определенной строки

Файл не основан на строках. Все прокручивается только такими маркерными строками. Поэтому я не могу использовать ReadLine.

Как я могу это сделать без чтения одного символа за раз и реализации конечного автомата для распознавания символов работы маркера? Я специально ищу метод, например StreamReader.SkipUntilAfter(string) или StreamReader.ReadUntil(string).

О, и этот проект по-прежнему использует .NET 2.0, поэтому LINQ здесь не нужен. Хотя я мог бы, вероятно, решить это, если кто-то предложит его использовать.

+0

Если файл основан на строках, вы можете использовать 'File.ReadLines' и LINQ (f.e.' SkipWhile', 'TakeWhile'). –

+0

Вы можете использовать что-то наподобие while ((line = reader.ReadLine())! = Null) {// ТОЛЬКО ПРОЧИТАТЬ ПОСЛЕ line.Equals ("")} – Crasher

+0

Обновленный вопрос: он не основан на линии. Маркер может встречаться в середине строки. Затем я уже прочитал часть данных.И StreamReader не может искать назад или искать куда угодно. – ygoe

ответ

1

TextReader s целом do уже прочитал только знак по характеру. Они используют буфер так, чтобы это было быстрее, но буфер для StreamReader не отличается от простого чтения вперед и тянет только до <eoh>. Также не будет лучшего способа пропустить до этого заголовка по той же причине. Абсолютным лучшим сценарием будет встроенная функция, которая просто визуально абстрагирует базовый код, поэтому это не особенно полезно.

Если вы не верите мне по какой-либо причине, here's the source code.

Кроме того, стоит отметить, что вам придется искать по-характер независимо от того, что. Даже если бы у вас был способ потянуть их в память, не делая этого, сравнение двух string s - это пошаговая операция. Поэтому вы ничего не спасете.

Лично я бы просто пошел с чем-то вроде этого. Он принимает строку TextReader и конец заголовка и читает через reader, пока не найдет eoh. Затем он возвращает bool для определения маркера или нет.

public bool SkipUntilAfterHeader(TextReader reader, string eoh) 
{ 
    int eohGuessIndex = 0; 
    int next; 

    while ((next = reader.Read()) != -1) 
    { 
     char c = (char)next; 

     if (c == eoh[eohGuessIndex]) 
     { 
      eohGuessIndex++; 
      if (eohGuessIndex == eoh.Length) 
      { 
       return true; 
      } 
     } 
     else 
     { 
      eohGuessIndex = 0; 
     } 
    } 

    return false; 
} 

Я не уверен, что .NET 2.0 было или не было, так что я написал некоторые вещи с нуля, что может или не может иметь быть. Но производительность не должна зависеть от этого. Хорошим аспектом этого является то, что вы также можете легко добавить StringBuilder с параметром out, который будет передавать информацию заголовка, если вы этого захотите позже.

Тогда использование довольно простое.

public void ReadFile(string path) 
{ 
    using (StreamReader reader = new StreamReader(path)) 
    { 
     if (SkipUntilAfterHeader(reader, "<eoh>")) 
     { 
      // read file 
     } 
     else 
     { 
      // corrupt file 
     } 
    } 
} 

Но, в действительности, проще всего прочитать весь файл и вернуть только соответствующую часть. Это зависит от того, насколько важна производительность, по сравнению с читабельностью.

И в классически плохой форме обратите внимание, что я не тестировал или даже не скомпилировал - все это. Но это должно быть относительно легко исправить, даже если оно не работает.

+0

Это будет работать, но чтение char-by-char намного интенсивнее, чем то, что StreamReader делает внутренне. – usr

+0

@usr нет, на самом деле это не так. Посмотрите на Reference Source для его реализации ['Read'] (http://referencesource.microsoft.com/mscorlib/a.html#5d81175d2e6d320e) и его реализации для [' ReadLine'] (http://referencesource.microsoft .com/mscorlib/a.html # a4ada5f765646068). 'ReadLine' эффективно делает то же, что и я, он немного оптимизирован. Но вы ничего не могли сделать здесь. Они оба используют внутренний буфер, поэтому в основном это только чтение в памяти. «Список <>» может быть легко улучшен, но я выбрал более расширяемый вариант. –

+1

Ваша горячая петля гораздо менее жесткая, чем StreamReader, потому что она может использовать свой внутренний буфер напрямую. Вызов Read для каждого символа - это то, что вы заметите в тестах. Также добавление и удаление списка добавляет дополнительные служебные данные. Я бы сказал, что ваша версия> = 3 раза медленнее, чем родная версия. – usr