С# лайфхаки для обработки больших файлов
Анализ больших объёмов данных – неотъемлемая и трудоёмкая часть аналитической деятельности. Наверняка вы сталкивались с проблемами, связанными с повышенным потреблением оперативной памяти и скоростью обработки. Представляем примеры решения подобных проблем на языке C#.
1. Нехватка памяти при чтении очень больших файлов
Предположим, нам необходимо найти соответствия регулярному выражению в строках CSV файла. В этом случае нам может помочь оператор yield return.
Этот оператор позволяет генерировать перечислимые коллекции элементов для их перебора в цикле. Его особенностью является то, что для перебора таких коллекций нам не нужно заранее хранить все их элементы в оперативной памяти компьютера. Это полезно, если заранее неизвестен размер обрабатываемого файла. Ниже приведён пример обработки csv-файла. Метод-генератор AllLinesFromFile (string a_file_path) принимает на вход путь к файлу и возвращает итератор, указывающий на текущий объект коллекции – связный список, состоящий не более, чем из ста строк файла. При этом в памяти одновременно будет находиться только один элемент коллекции. Таким образом, мы можем обрабатывать файл частями, не загружая его полностью в оперативную память.
Теперь продемонстрируем в методе Main() использование написанного выше генератора коллекции для чтения csv файла, состоящего из 437 строк:
Примечания
- Размер возвращаемой «порции строк» взят не более ста (исключительно для примера). На практике выбор этого значения должен быть обусловлен системными требованиями к потреблению памяти приложением.
- Показанный способ чтения текста из файлов подходит для простых форматов (.txt или. csv). Для чтения более сложных форматов, например JSON или XML, может дополнительно понадобиться использование соответствующего парсера.
- В качестве типа возвращаемого элемента коллекции (на который указывает итератор) метод AllLinesFromFile (string a_file_path) использует связный список строк, что также поможет избежать возможных проблем с выделением непрерывной области памяти (если бы использовался массив).
- Подробнее про оператор yield return в C# можно почитать на официальном сайте Microsoft (https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/keywords/yield).
- Оператор yield есть не только в языке C#, но и во многих других языках программирования, например Python, JavaScript, PHP и др.
2. Использование LINQ и PLINQ
Платформа .NET содержит много полезных встроенных библиотек, в том числе и для работы с данными. Например, LINQ (Language-Integrated Query) и PLINQ (Parallel LINQ). Эти технологии позволяют совершать разработчику запросы к различным данным (коллекциям C#, БД, XML файлам и др.), используя универсальный язык.
Остановимся именно на использовании PLINQ. Данный инструмент является параллельной реализацией LINQ и может использоваться при работе с коллекциями C#. При применении запроса к элементам коллекции он будет автоматически распараллеливаться (при условии, что это поможет ускорить выполнение запроса), разбивая коллекцию на сегменты, каждый из которых будет обрабатываться в отдельном потоке. Деление осуществляется, исходя из доступных системных ресурсов. Например, нам необходимо найти все файлы с указанными расширениями в папке и всех её дочерних папках. Можно написать рекурсивный алгоритм, который бы последовательно перебрал все необходимые папки с файлами. Но с помощью библиотеки PLINQ можно выполнить эту операцию параллельно:
Примечания
- C помощью метода AsParallel() массив расширений файлов SearchPatterns разбивается на части, каждая из которых обрабатывается в отдельном потоке.
- В свою очередь, для каждой из полученных частей выполняется запрос LINQ (метод SelectMany()), запускающий метод EnumerateFiles() для поиска файлов с нужным расширением.
- Метод Directory.EnumerateFiles() находит нужные файлы и возвращает пути к ним с помощью оператора yield return (что полезно в случае, когда неизвестно заранее, сколько файлов будет удовлетворять условиям поиска).
В топку этот ваш C#. Да и эпоха виндузятников уже практически завершилась.
Изучайте классику - всякое там C, C++ и системные вызовы ядра - теперь за это лучше кормят.
С чего бы? С++ еще живет? Это скорее- изучайте JS - он теперь везде
Да, уже не так часто его используем. Но полностью пока не отказались.