Лучшая стратегия для реализации ридера для больших текстовых файлов - PullRequest
2 голосов
/ 01 марта 2012

У нас есть приложение, которое записывает свои шаги обработки в текстовые файлы. Эти файлы используются во время реализации и тестирования для анализа проблем. Каждый файл имеет размер до 10 МБ и содержит до 100 000 строк текста.

В настоящее время анализ этих журналов выполняется путем открытия средства просмотра текста (Notepad ++ и т. Д.) И поиска конкретных строк и данных в зависимости от проблемы.

Я создаю приложение, которое поможет анализу. Это позволит пользователю читать файлы, выполнять поиск, выделять определенные строки и другие конкретные операции, связанные с выделением соответствующего текста.

Файлы не будут редактироваться!

Немного поиграв с некоторыми концепциями, я сразу обнаружил, что TextBox (или RichTextBox) не очень хорошо справляются с отображением большого текста. Мне удалось реализовать средство просмотра с использованием DataGridView с приемлемой производительностью, но этот элемент управления не поддерживает выделение цветом определенных строк.

Теперь я думаю о том, чтобы сохранить весь текстовый файл в памяти в виде строки и отображать только очень ограниченное количество записей в RichTextBox. Для прокрутки и навигации я подумал о добавлении независимой полосы прокрутки.

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

Если у кого-то есть идеи, можете выделить проблемы с моим подходом, тогда спасибо.

Ответы [ 4 ]

4 голосов
/ 01 марта 2012

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

string[] lines = File.ReadAllLines("file.txt");

Затем вы можете искать подходящие строки с помощью LINQ, легко их отображать и т. Д.

3 голосов
/ 01 марта 2012

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

Вы создаете блок итератора, который выдает строки из текстового файла (или нескольких текстовых файлов, если требуется):

IEnumerable<String> GetLines(String fileName) {
  using (var streamReader = File.OpenText(fileName))
    while (!streamReader.EndOfStream)
      yield return streamReader.ReadLine();
}

Затем вы используете PLINQ для параллельного поиска строк.Это может значительно ускорить поиск, если у вас современный ЦП.

GetLines(fileName)
  .AsParallel()
  .AsOrdered()
  .Where(line => ...)
  .ForAll(line => ...);

Вы предоставляете предикат в Where, который соответствует строкам, которые необходимо извлечь.Затем вы предоставляете действие ForAll, которое отправит строки в их конечный пункт назначения.

Это упрощенная версия того, что вам нужно сделать.Ваше приложение является приложением с графическим интерфейсом, и вы не можете выполнять поиск в основном потоке.Для этого вам придется запустить фоновое задание.Если вы хотите, чтобы эта задача была отменяемой, вам нужно проверить токен отмены в цикле while в методе GetLines.

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

В этом решении предполагается, что вы можете извлечь нужные строки, выполнив одну прямую передачу файла.Если вам нужно сделать несколько проходов, возможно, на основе пользовательского ввода, вам может потребоваться вместо этого кэшировать все строки из файла в памяти.Кэширование 10 МБ это не много, но допустим, вы решили искать несколько файлов.Кэширование 1 ГБ может привести к нагрузке даже на мощный компьютер, но использование меньшего объема памяти и больше ресурсов ЦП, как я полагаю, позволит вам искать очень большие файлы в разумные сроки на современном настольном ПК.

2 голосов
/ 01 марта 2012

Я полагаю, что, когда у вас есть несколько гигабайт оперативной памяти, естественно, тяготеет к пути «загрузить весь файл в память», но действительно ли кто-нибудь здесь действительно удовлетворен таким поверхностным пониманием проблемы? Что происходит, когда этот парень хочет загрузить файл размером 4 гигабайта? (Да, вероятно, маловероятно, но программирование часто связано с масштабируемыми абстракциями, и быстрое исправление загрузки всего этого в память просто не масштабируется.)

Конечно, существуют противоречивые проблемы: вам нужно вчерашнее решение или у вас есть время, чтобы разобраться в проблеме и узнать что-то новое? Каркас также влияет на ваше мышление, представляя файлы блочного режима в виде потоков ... вы должны проверить значение BaseStream.CanSeek потока и, если это так, получить доступ к методу BaseStream.Seek (), чтобы получить произвольный доступ. Не поймите меня неправильно, мне очень нравится .NET Framework, но я вижу строительную площадку, где кучка «плотников» не может поднять каркас дома, потому что воздушный компрессор сломан, а они - нет. уметь пользоваться молотком Воск, воск, научить человека ловить рыбу и т. Д.

Так что, если у вас есть время, посмотрите в раздвижное окно. Вы, вероятно, можете сделать это простым способом, используя отображенный в памяти файл (пусть framework / OS управляет скользящим окном), но самое интересное решение - написать его самостоятельно. Основная идея заключается в том, что в каждый момент времени в память загружается только небольшая часть файла (часть файла, которая видна в вашем интерфейсе, возможно, с небольшим буфером с обеих сторон). По мере продвижения по файлу, вы можете сохранять смещения начала каждой строки, чтобы вы могли легко найти любой более ранний раздел файла.

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

[/ декламация]

1 голос
/ 18 апреля 2012

Я бы предложил использовать MemoryMappedFile в .NET 4 (или через DllImport в предыдущих версиях) для обработки только небольшой части файла, которая видна на экране, вместо того, чтобы тратить память и время на загрузку всего файл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...