crower: (Crower)
[personal profile] crower
Подарил себе код. Почти законченный. Допилил — получил удивительное чувство удовольствия.


А получилось так.
Есть у меня старенький парсер. Написанный надцать лет назад. На С. Постепенно подправлял, но кардинально идеологию не поправишь. Задача его — парсить тарифные файлы. Фишка (ради чего его начинал писать чтобы заменить предыдущий) в гибкости. Станций дофига, на каждой настройки могут быть свои. Даже на одной и той же станции может возникнуть необходимость что-то изменить и сиди ковыряй исходники. За время эксплуатации фича эта понадобилась с десяток раз. И была весьма ценной, так как существенно экономит время и нервы. Но есть у этого парсера и некоторые недостатки. Большая часть из которых связана со спецификой программирования. У меня нет возможности и условий спокойно сеть за код и несколько часов кряду заниматься только им. По сути дела при подобной работе необходимо в голове держать некоторую модель, постоянно более-менее её контролировать и корректировать при внесении изменений. В самом начале моей работы такие условия были. Позже — нет. Меня могут отвлечь по другому вопросу. И не по одному. В процессе решения третьего может понадобиться заняться внесением изменений в код другого софта. Отладки и поиска бага в третьем. Потом необходимо вернуться к первому и продолжить работу. В идеале — «как ни в чём не бывало». И эти условия программирования если имеют не первостепенное значение, то одно из главных. Это накладывают определённые требования к проектированию, структуре софта и используемым алгоритмам, «патернам программирования» ;) и языку. Поэтому, кстати, очередная переделка робота у меня буксует. :( :)

Этот старый парсер писался тоже не за один присест и по мере написания расширялись возможности. Тип такой эволюции. ;)
Так как в исходных файлах встречаются разные типы записей, то идея сделать настраиваемый формат ввода/вывода пригождалась и здесь: смотрим тип записи, выбираем нужный шаблон и по нему строим вывод, подставляя вместо обозначаемых параметров распарсеные значения.
Но когда нужно разобрать один файл (для решения технического вопроск), то минута погоды не делает. Для поиска одной или нескольких записей тоже можно потерпеть пока парсер не разберёт все файлы за сутки. Но когда существует потребность проделать это с файлами хотя бы за месяц, то… Нет, конечно, всё скриптами решается, а после парсинга классический grep выберет всё что нужно. Но время на это уходит и когда хочется чтобы было быстрее думаешь как сделать быстрее. «Простой» вариант — предварительный парсинг: автоматом по мере поступления файлов парсим в удобную форму и грузим в DB. Но, во-первых, объём получается очень большим. Чтобы бороться с этой напастью исходные файлы я вообще жму (bzip2 -9 утрамбовывает в 10-12 раз). Даже если обойтись загрузкой в БД только самых нужных параметров, то до миллиона записей ежесуточно плюс индексы на оставшиеся поля — весьма прожорливое хозяйство получается. Поэтому этот подход постоянно откладывался. Впрочем, как временное решение в БД я грузил записи, чтобы с ними там работать. И многократно убеждался что это «ну очень долго» :) и пока не нарисован специальный интерфейс — довольно неудобно. В то время как парсер генерит вывод по шаблону, формат которого «выстрадан» многими годами эксплуатации и экспериментов. Так что предварительный парсинг и загрузка в БД даёт только один выигрыш — кажущуюся быстроту получения результата после запроса.
Второй вариант, когда нужно найти что-то конкретное — сначала искать, а только затем парсить. Файлы состоят из блоков фиксированного размера. В каждый блок напихано сколько поместится записей. Алгоритм простой: перебираем блоки и ищем искомую подстроку в каждом. Парсим блок только если нужная строка нашлась. Когда искомых записей единицы, то оптимизация работает на ура.
Кстати, предварительный поиск я делал уже на perl. В описанных ранее условиях он подходит гораздо лучше чем C. По быстродействию может и уступает, но по сопровождению кода и вхождению в процесс уступает сильно.
Если же их количество искомых записей велико, то относительная эффективность предварительного поиска начинает падать.

Кстати, как-то с одним товарищем мерялись зачёркнуто эффективностью алгоритмов. У него читался каждый параметр записи отдельно. У меня читается блок (в блок умещается несколько записей, с полтыщи параметров). Пытался спорить что ОС все равно всё кеширует. Потестировали ­— «ушёл посрамлённый», но пытаясь делать мину что это всё не важно. В моих условиях это не имеет решающего значения, но бывает весьма важно.

Дальнейшие размышления привели к наиболее нагруженному месту. Цена гибкости — постоянная перегрузка шаблона, поиск в нём помеченных параметров, замена на значения и т.д. Напрашивалось решение: парсим шаблон заранее и один раз. Получаем список ссылок на (a)строки/части шаблона и (b)параметры. Потом приходит мысль что парсить тоже можно более эффективно. В общем, дело оставалось за главным — сесть и написать. Садился, начинал, потом отвлёкся…

Несколько раз «пробегал» мимо кода, вспоминая что начинал, но всё было некогда продолжить. И вот опять созрел вопрос сделать загрузку в базу «по-быстрому». Сишным вариантом продолжаю пользоваться для технических нужно, но он иногда встречает проблемные файлы и стреляется. Лечить не реально. Не хватит времени (необходимого неквантованного). Подумал: "А не сделать ли мне быстренько парсер на перле?". Пусть без гибких шаблонов. И пока я об этом думал, как раз опять набрёл на тот самый неоконченый парсер. Глянул — внутри какой-то алгоритм уже набросан: чтение файла, разбор по блокам, по записям, парсинг. "Ну", - думаю: "хорошо. Можно использовать костяк". Только начал выбрасывать «лишнее», как обнаруживаю и шаблоны, и полную загрузку конфигурации. Да логи какие-то есть. Не… если это уже есть может допилить? Стал смотреть почему вываливаются ошибки (а по сути предупреждения). Стал править. Мать чесная! А парсер-то, оказывается, почти закончен. Все ошибки оказались связаны с нестрогой типизацией формата файла. Вот есть параметр, который декларирован как десятичное число. Значит его значение должно быть в ISOCODED цифрой, добитое пробелами. Нормальные такие параметры будут нулём если значения нет, а одно такое оказалось пустым. Впрочем, одно я ещё в самом начале самовольно переопределил под простой текст, поскольку всё равно не используется. И ещё несколько ошибок (тоже не фатальных) вываливалось из-за того что в исходном формате шаблона помимо нумерованных параметров у меня были именованные. И вот в этом коде именованные я не заимплементил. Может не знал как лучше сделать, а может просто не успел. Неудобство ещё было в том что эти параметры требуют определённых преобразований исходного формата. Для начала просто заменил именованные параметры на нумерованные. Пришлось отказаться от некоторых "фич", которые, впрочем, не сильно-то и нужны. Хотя некоторые идеи как засапортить именованные уже появились.

Начинал парсер два года назад. Потом, похоже, перед отпуском не доделал и отложил. И постепенно забыл о нюансах.
Теперь нашёл и вот сколько удовольствия получил. Самое смешное, что когда сейчас ковырял некоторые места обнаружил много интересных и оригинальных решений. И всё восхищался собой: «Надо же как здорово я здесь придумал». :D То есть именно сейчас по этому поводу такие идеи мне не приходили. Может быть помедитируй я ещё с недельку (а не два дня), я бы придумал или то же самое, или что-то другое (но тоже не плохое), а может и ещё лучше. И вот эта возможность оценить свои идеи «со стороны» меня очень впечатлила.

В итоге новый парсер на perl работает быстрее старого на C в 3 раза. На том же файле, с тем же набором выводимых параметров…
Праздник. :)

И это после того как накануне допилил скрипты для работы с фотками по поиску Марса-6. И параллельно допилил квартального статиста — анализирует статистику и выдаёт полную сводку за текущий квартал какие показатели достигнуты, на сколько какие изменились по сравнению с предыдущим кварталом, и то же самое с прошлогодним… Вот она — программистская радость. :)

Profile

crower: (Default)
crower

February 2018

S M T W T F S
    123
45678910
11121314151617
181920212223 24
25262728   

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 28th, 2026 02:39 am
Powered by Dreamwidth Studios