Практика регулярных выражений в 1С или "парсим неудобные форматы"

Программирование - Практика программирования

В продолжение статьи Использование регулярных выражений (RegExp) в 1С8.х. Углубляемся в практику использования регулярных выражений в 1С. Основы работы с регулярными выражениями хорошо описаны в указанной публикации. А я попробую ответить на вопрос "почему именно регулярные выражения?" на примере конкретной рабочей задачи.

В продолжение статьи Использование регулярных выражений (RegExp) в 1С8.х. Углубляемся в практику использования регулярных выражений в 1С. Основы работы с регулярными выражениями хорошо описаны в указанной публикации. А я попробую ответить на вопрос "почему именно регулярные выражения?" на примере конкретной рабочей задачи.

Удобный и неудобный

Прежде чем приступить к непосредственному разбору практической задачи хотелось бы внести ясность в понятия "удобный" и "неудобный" формат на примере xml и html. Первый (xml - расширяемый язык разметки) предназначен именно для хранения информации, в то время как последний (html - язык разметки гипертекста) предназначен для структурированного отображения информации.

"В чем же такая большая разница? И там и там язык разметки." - резонно заметите вы. Все дело в значимых тегах. Теги xml предназначены для идентификации определенного в них содержимого. Например:

Пример 1. Иванов

Слово "Иванов", заключенное в тег для читающей xml-файл системы будет означать, что Иванов - автор, со всеми вытекающими последствиями. xml потому и называется "расширяемым", что названия тегов разработчик вводит сам с учетом потребностей по хранению/передаче определенной информации.

Теги html служат для того, чтобы сказать читающей системе (браузеру), как нужно визуализировать (отобразить в окне браузера) те или иные данные. Теги html конечны, т.е. разработчик сам не может придумать свой тег, иначе браузер его не поймет. Например:

Пример 2. Иванов

Для браузера будет означать, что слово "Иванов" отображается в окне браузера как новый параграф с соответствующими отступами и т.п. Из этого мы вовсе не знаем, что Иванов - автор. Очевидно, что html - неудобный для парсинга (разбора) формат.

Справедливости ради надо заметить, что html может стать частично удобным форматом если тегам начать присваивать классы или идентификаторы. Если в нашем случае будет написано например так:

Пример 3. Иванов

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

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

Почему регулярные выражения?

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

"Почему именно регулярные выражения?" - сросите вы. Ведь можно использовать штатные средства платформы 1С "Найти", "СтрДлина", "СтрЗаменить" и пр. Да, можно, но программный код с использованием регулярных выражений локоничнее и понятнее. Для того, чтобы идентифицировать определенные данные в строке, нужно будет написать кучу "Если", "Найти", "СтрЗаменить" и т.д. В регулярных выражениях весь этот массив кода можно заменить одним шаблоном (паттерном).

Ключевым моментом при работе с регулярными выражениями является выявление уникальной комбинации строковых выражений. Именно комбинация определенных строковых выражений и служит идентификацирующим признаком значимых данных. Поясню. Например, встречая в разбираемом файле такую комбинацию тегов:

Пример 4. Стругацкие

знаем, что в первом теге h1 содержится название автора произведения, во втором теге h2 содержится название произведения, а в третьем теге p содержится аннотация к произведению. Т.е. каждый раз встречая в файле набор этих тегов я заранее знаю что находится в первом, втором и третьем тегах. Повторюсь - здесь важно знать, что комбинация уникальна, т.е. не должно быть другой такой же комбинации тегов, в которых содержится какая-то другая информация или присутствует другой порядок данных. Это знание и регулярные выражения позволяют извлекать из таких вот уникальных комбинаций нужные данные.

Практика

Ничто так не позволяет усваивать материал, как практика.

Ниже на скриншоте вы можете наблюдать фрагмент отрисованного в браузере счета, присланного в формате html:

Фрагмент счета

"Чудесно, это же всего-навсего таблица и распарсить ее не составит труда" - подумаете вы. Как бы не так. Не стоит забывать, что это всего лишь фрагмент, до и после которого есть уйма всякой сопутствующей информации, как-то разрывы строк, колонтитулы, итоги, заголовки страниц и пр. и пр., вобщем структурированный хаос информации. И такого хаоса мегабайт на 200.

А теперь вот как одна из строк выглядит в html коде (строка длинная, поэтому я сделал перенос строк и обозначил места переносов символом "|"):

Пример строки в коде HTML

Кто разбирается в HTML знает, что тег tr обозначает строку в таблице, а td обозначает колонку, точнее ячейку в определенной колонке определенной строки.

Парсить такое штатными средствами 1С весьма затруднительно, проще написать шаблон на языке регулярных выражений и текст исходного кода сокращается в разы. Ниже пример разбора в 1С такого кода с помощью регулярных выражений (обращаю внимание, что это лишь фрагмент кода и я не акцентирую внимание на создание в 1С объекта для работы с регулярными выражениями - это итак хорошо описано в обозначенной мной выше статье):

Пример кода 1С для разбора строки HTML при помощи рег. выражений

Это собственно весь разбор. Далее уже следует работа с извлеченными данными.

Если при разборе строки все условия регулярного выражения выполнены - это значит, что мы разбирали именно строку детализации, а значит в МассивеСовпадений располагаются в порядке очередности нужные данные (дата/время, номер собеседника, количество, стоимость и т.д.)

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

Расшифровка регулярного выражения

  1. Ищется тег td внутри которого может присутствовать описание классов, атрибутов и т.п.
  2. Ищется последовательность цифр и символов: 2 цифры, точка, 2 цифры, точка, 4 цифры, пробел, 2 цифры, двоеточие, 2 цифры, двоеточие, 2 цифры. Это ничто иное как дата и время. Причем все, что находится в круглых скобках запоминается и затем попадет в МассивСовпадений, за исключением тех скобок, в которых сначала идет ?:.
  3. Ищется закрывающий тег td, после которого идет произвольное количество тегов td до выполнения следующего по выражению условия.
  4. Далее по условию следует либо любой символ, кроме символа >, либо символ "пусто". Пусто, потому что в ячейке может быть пусто, что будет обозначено специальным символом. Здесь производится поиск номера собеседника.
  5. Далее по условию должны встретиться одна из букв F, D, S либо знак "пусто" - это тип звонка.
  6. Далее по условию должны встретиться либо 1 цифра, либо 2 цифры, либо знак "пусто" - это код звонка.
  7. Далее по условию следует от 1 до 6 цифр, точка,  1 или 2 цифры. Причем последние две цифры необязательные. Это длительность звонка (количество).
  8. Далее по условию должны встиретиться любое количество русских букв, цифр или пробелов до выполенения следующего условия выражения. Это строковое представление единицы измерения длительности звонка.
  9. Далее по условию следуют от 1 до 9 цифр и в обязательном порядке точка и 2 цифры. Это стоимость звонка.
  10. Ну и напоследок закрывающий тег td.

См. также

Комментарии
1. Александр Рытов (Арчибальд) 2659 26.10.11 09:36 Сейчас в теме
Совершенно мне не требуется. Но не могу не оценить "красоту игры" (© Ю. Ким)
2. Ийон Тихий (cool.vlad4) 41 26.10.11 10:56 Сейчас в теме
автор, молодец, но плюс я ставить не буду. В общем случае html не советуют парсить регулярными выражениями, очень часто будут ошибки. Приводят html к валидному xml, а дальше xpath или xslt, если хочется. В редких случаях, я допускаю использование рег.выражений для быстрого и за один раз - извлечения данных.
3. Armando Armando (Armando) 1375 26.10.11 11:11 Сейчас в теме
Хочу чтоб регекспы сделали объектом языка 1С, тогда ваще кошерно было бы.
РегулярноеВыражение = Новый РегулярноеВыражение;
kraynev-navi; ekomova; 1cspecialist; +3 Ответить 2
4. Денис (1cspecialist) 1341 26.10.11 12:06 Сейчас в теме
(2) Не совсем понял, чем вам поможет xpath или xslt в этом случае - при приведении html к валидному xml вы столкнетесь абсолютно с теми же вопросами, как и при парсинге html регулярными выражениями. Да и само утверждение "html не советуют парсить регулярными выражениями" весьма спорно - это все равно что сказать, что не рекомендуют автоматизировать "Газпром" на 1С. Почему нет? Нужно рассматривать каждую ситуацию в отдельности. Регулярные выражения - это такой же инструмент как и многие другие.
5. Денис (1cspecialist) 1341 26.10.11 12:10 Сейчас в теме
(2) вообще, я да и многие другие были бы признательны, если бы вы написали тут статью про парсинг с помощью xpath, xslt и приведение html к валидному xml - просто уж очень тема интересная
6. Ийон Тихий (cool.vlad4) 41 26.10.11 12:11 Сейчас в теме
(4) я вроде ничего, такого не написал, даже похвалил вас. не нарывайтесь. Эти глупые сравнения не в счет, поскольку я нигде не писал, что от регулярных выражений надо отказыватся. А не советуют пользоватся для парсинга html вполне серьезные программисты из stackoverflow. Я куда больше доверяю, чем вам. xpath и xslt не тоже самое, не знаете, не говорите.
7. Ийон Тихий (cool.vlad4) 41 26.10.11 12:12 Сейчас в теме
(5) не обещаю, но может быть.
9. Герман (German) 932 26.10.11 12:31 Сейчас в теме
(3) да и будет вам как "Внешние источники данных"
(5) про Xpach тут можно пример посмотреть тут http://main.1c-ei.ru/Home/help/console/template/xml
а парсинг HTML лучше доверить специализированным но очень редким уже(!) вещам, например http://blog.1c-ei.ru/2009/09/openkapow.html
1cspecialist; +1 Ответить
10. Ийон Тихий (cool.vlad4) 41 26.10.11 12:40 Сейчас в теме
на самом деле просто писать статью, с целью обучения, конечно, благородно, но я не могу себя заставить. У меня идея и потихоньку я её реализовываю, сделать обычный прокси, через который, в зависимости от опций и можно будет получать/парсить/кэшировать/приводить в xml и т.д. - тогда можно будет и из 1С-ки это делать. Для знакомства с xpath - поставьте расширение для chrome/firefox (firepath например). Единственно, нужно помнить, что webkit-овские движки и mozilla, добавляют некоторые изменения - например <tbody>, в таблицы, которого нет. Поэтому xpath может быть неточным в браузере.
11. Денис (1cspecialist) 1341 26.10.11 13:03 Сейчас в теме
(10) что такое xpath я представляю, реализация объекта для работы с xpath есть и в 1С, но у этой методики есть также и минус - для работы требуется построение DOM дерева, по которому xpath будет ходить, а это требует ресурсов оперативки, хотя наверное на мощных серверах это и не такой уж и большой минус

то что вы кому-то доверяете, а кому-то нет - ваше право, но я хотел просто услышать, почему не рекомендуется использовать регулярные выражения, как решается проблема идентификации данных? был бы признателен, если бы вы тезисно изложили суть.
12. Ийон Тихий (cool.vlad4) 41 26.10.11 13:13 Сейчас в теме
(11) для этого надо либо прочесть ссылки, которые я дал выше, либо понять, что такое регулярное выражение(в статье об этом ни слово). html не относится к регулярным языкам. Есть такая теорема любой регулярный язык представим в виде регулярного выражения. html нет.
13. Ийон Тихий (cool.vlad4) 41 26.10.11 13:22 Сейчас в теме
(11) и почему есть DOM, если regexp так хорош? Сколько вы спарсили сайтов? Кто гарантирует вам, что если для пару страниц ваша регулярка сработает, то и для 1000 страниц тоже? Почему DOM сработает? да потому, что вы парсите те участки, которые занимают вполне определенное положение в дереве иначе смысла нет.И тем не менее я не исключаю регулярки, я использую и то, и то. Просто в статье посыл другой.
14. Денис (1cspecialist) 1341 26.10.11 13:26 Сейчас в теме
(13) ну я так примерно и представлял, все равно спасибо за ответ
15. Дмитрий Осин (theologian) 26.10.11 15:57 Сейчас в теме
16. denis leonov (curys) 27.10.11 11:32 Сейчас в теме
хорошая штука, благодарю
17. Инга (byuf_in) 27.10.11 11:40 Сейчас в теме
спасибо, как раз поставили задачу, где можно применить
18. Сергей (Seregalink) 72 28.10.11 00:31 Сейчас в теме
19. Michael Smith (opiumdx) 28.10.11 14:56 Сейчас в теме
20. Олег Шалимов (CaSH_2004) 343 28.10.11 22:43 Сейчас в теме
Думаю у любого инструмента есть минусы, но с непривычки легче пользоваться более простыми инструментами, а потом переходить к более сложным. Также важно знать какие минусы есть. Поэтому спасибо и автору и cool.vlad4 за критику (правда она какая-то злая, наверно столкнулся с этими проблемами сам :)
21. Александр Маляев (maljaev) 769 29.10.11 12:19 Сейчас в теме
Прочитал пост 13, вот абсолютно соглашусь с его автором - использую и то и другое в определенных случаях, но сам RegExp не панацея, тем более относительно HTML (DOM лучше). Но в определенных ситуациях RegExp рулит.
22. Денис (1cspecialist) 1341 29.10.11 13:48 Сейчас в теме
(21) regexp спасет в большинстве случаев, особенно когда нужно оптимизировать производительность в условиях ограниченных ресурсов. Попробуйте в браузере открыть файл html размером 200 мб (и это далеко не самый большой файл, который приходится парсить) и посмотрите в диспетчере задач windows - он сожрет у вас больше 2 гигабайт оперативной памяти на построение DOM-модели документа. Тоже самое будет и с любой другой программой, использующей DOM для своей работы. Если у вас на компьютере 4 Гб и/или 32-разрядная ось то легко получите out of memory. Т.е. предложенный вариант с xpath будет еле ворочиться, если вообще зашивелится.

PS. Конечно xpath хороший и надежный выбор, но говорить, что regexp ненадежен - тоже нельзя. Еще раз повторюсь - нужно смотреть на условия конкретной задачи. В большинстве случаев regexp прекрасно справится с увесистыми файлами и причем не на самых мощных компьютерах. Если речь идет просто о парсинге сайтов, то конечно лучше использовать xpath, но с файлами с большим объемом лучше использовать механику последовательного чтения и парсинга, что собственно как нельзя лучше осуществляет regexp.
23. Игорь (vikorn) 03.11.11 13:08 Сейчас в теме
Нужная вещь, скопировал себе, спасибо
24. Евгений L (laeg) 13 12.11.11 10:00 Сейчас в теме
Одно время, писал на 1с-ке парсер товаров с нескольких интернет магазинов
тупое перебирание текста, поиск по тегам не есть гуд. Малейшие изменение в дизайне и все коту под хвост.

Спасибо за статейку, при дальнейшей разработке, попробую использовать ваши наработки.
1cspecialist; +1 Ответить
25. Андрей Пастухов (Minotavrik) 1467 16.11.11 11:28 Сейчас в теме
И не жалко тратить время на это? Во времена ассемблера и с статья была бы бесценна. В коде много других участков, которые надо думать как оптимизировать. А вот работа со строками в 1с вроде реализована и довольно не плохо.

Но статья клевая снимаю шляпу за усердие.
26. Дмитрий Титов (dtitov) 28.11.11 11:23 Сейчас в теме
27. Юлия Петрова (petrovaUL) 08.12.11 14:53 Сейчас в теме
28. Валентин Елфимов (ratinc) 17.01.12 10:26 Сейчас в теме
Жаль что regexp у vbscript куцый.
Никак не удастся развернуться в полную силу.
Сильно не хватает следующего:
No \A or \Z anchors to match the start or end of the string. Use a caret or dollar instead.
Lookbehind is not supported at all. Lookahead is fully supported.
No atomic grouping or possessive quantifiers
No Unicode support, except for matching single characters with
No named capturing groups. Use numbered capturing groups instead.
No mode modifiers to set matching options within the regular expression.
No conditionals.
No regular expression comments. Describe your regular expression with VBScript apostrophe comments instead, outside the regular expression string.

Может можно ещё к чему com поиметь с "нормальным" regexp?
Буду признателен за помощь в поиске альтернативы
PS. Майкрософт как обычно реализует "свои" стандарты вот и regexp-у досталось :)
29. Сергей Писларь (serpisal) 14.02.12 11:51 Сейчас в теме
Хорошее решение, спасибо!
30. Дмитрий Веселов (Veduin) 15.02.12 08:09 Сейчас в теме
Интересно и познавательно! Спасибо!
32. Валентин Елфимов (ratinc) 28.03.12 12:20 Сейчас в теме
Что то тема совсем заглохла. Оскудела земля русская программистами :)
Ну неужели никто не подскажет откуда ещё кроме vbscript можно regexp поиметь?
33. Сергей Вн (EmpireSer) 09.08.12 23:00 Сейчас в теме
(32) ratinc, от delphi. Они там его от каких-то С++ библиотек прикрутили.
Оставьте свое сообщение