Внешняя компонента для выполнения регулярных выражений

Внешняя Native API компонента для выполнения регулярных выражений на платформе 1С:Предприятие 8.
Написана на C++. Используется движок boost::regex (v 1.69, v 1.68 — для Android).
Версия синтаксиса Perl Compatible Regular Expressions.

Внимание! В текущей версии нет совместимости с Windows XP (совместимость была до версии 4).

Текущая версия собрана для следующих платформ:
Windows 32bit 

Windows 64bit 

Linux 32bit 

Linux 64bit

MacOS 64bit

Android ARMv7-A

Android x86

Google Chrome (Linux, Windows)

Тестировалось на платформе 8.3.12.1567 (Windows 7, Windows Server 2008 R2, Ubuntu 14 32-64bit, MacOS Sierra 10.12, Android 8)

Адрес open-source проекта на GitHub: https://github.com/alexkmbk/RegEx1CAddin

Бинарные файлы можно скачать также с GitHub: https://github.com/alexkmbk/RegEx1CAddin/releases

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

Сборка осуществлялась с использованием следующих инструментов:

Под Windows: Microsoft Visual Studio Community 2024

Под Linux: GCC 6

Под Mac OS: Clang 9   

Под Android: Android Studio NDK 19.2

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

Компонента реализует следующие методы и свойства:

Метод НайтиСовпадения / Matches(<Текст для анализа>, <Регулярное выражение>)

Метод выполняет поиск в переданном тексте по переданному регулярному выражению.

Результатом выполнения метода будет массив результатов поиска. Каждый элемент массива — найденная подгруппа поиска. Если подгрупп нет, то массив будет содержать один элемент — найденную строку.

Возвращаемое значение: Ничего не возвращает.

Для того, чтобы получить результаты выполнения метода (массив результатов), необходимо выполнить метод Следующий/Next(), и после этого, в свойстве ТекущееЗначение/CurrentValue будет доступно значение текущей подгруппы результата выполнения (текущий элемент массива результатов). Идея  похожа на обход результата запроса.

Пример:

Рег.НайтиСовпадения("Hello world", "([A-Za-z]+)s+([a-z]+)");

Пока Рег.Следующий() Цикл
Сообщить(Рег.ТекущееЗначение);
КонецЦикла; 

Результат будет содержать 3 элемента:

Hello world
Hello
world

Метод Количество/Count()

Возвращает количество результатов поиска, после выполнения метода НайтиСовпадения / Matches

Метод Заменить/Replace(<Текст для анализа>, <Регулярное выражение>, <Значение для замены>).

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

Возвращаемое значение: Строка, результат замены.

Метод Совпадает/IsMatch(<Текст для анализа>, <Регулярное выражение>)

Делает проверку на соответствие текста регулярному выражению.

Возвращаемое значение: Булево. Возвращает значение Истина если текст соответствует регулярному выражению.

Метод Версия/Version()

Возвращает номер версии компоненты в виде строки.

Возвращаемое значение: Строка

Свойство ВсеСовпадения/Global

Тип: Булево.

Значение по умолчанию: Ложь.

Если установлено в Истина, то поиск будет выполняться по всем совпадениям, а не только по первому.

Свойство ИгнорироватьРегистр/IgnoreCase

Тип: Булево.

Значение по умолчанию: Ложь.

Если установлено в Истина, то поиск будет осуществляться без учета регистра.

Свойство Шаблон/Template

Тип: Строка.

Значение по умолчанию: пустая строка.

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

Свойство ОписаниеОшибки/ErrorDescription

Тип: Строка.

Значение по умолчанию: пустая строка.

Содержит текст последней ошибки. Если ошибки не было, то пустая строка.

Свойство ВызыватьИсключения/ThrowExceptions

Тип: Булево.

Значение по умолчанию: Ложь.

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

Пример использования:

Предполагается что архив с компонентами был загружен в общий макет "RegEx"

УстановитьВнешнююКомпоненту("ОбщийМакет.RegEx");
ПодключитьВнешнююКомпоненту("ОбщийМакет.RegEx", "Component", ТипВнешнейКомпоненты.Native);

Рег = Новый("AddIn.Component.RegEx");
Рег.НайтиСовпадения("Hello world", "([A-Za-z]+)s+([a-z]+)");

Пока Рег.Следующий() Цикл
Сообщить(Рег.ТекущееЗначение);
КонецЦикла;

Сообщить(Рег.Количество());
Сообщить(Рег.Совпадает("Hello world", "([A-Za-z]+)s+([a-z]+)"));
Сообщить(Рег.Заменить("Hello world", "([A-Za-z]+)s+([a-z]+)", "Текст для замены"));

 

57 Comments

  1. Dzenn

    Буду гадом 😉 На инфостарте есть что-то про регулярные выражения в 1С на основе механизмов работы с XML. То есть, помещаем в поле-HTML нашу строку, обёрнутую в тэги, запускаем встроенную функцию и получаем ответ. Но не уверен, что это аналог, с регулярками особо не приходилось работать.

    Reply
  2. zykov_vitaliy

    Использую

    Regexp = Новый COMОбъект(«VBScript.RegExp»);

    Reply
  3. KAV2

    (2) Данная компонента работает также под Linux. В планах собрать под MacOS если будут время и силы.

    Reply
  4. ImHunter

    (1) Есть такое, через XDTO делается. Но такой прием работы предназначен только для валидации строки к паттерну.

    Т.е. количество и/или место вхождения паттерна — так не найти.

    Reply
  5. A_Max

    (2) этой штуки нет в linux, а так всё необходимое имеем при себе в базе!

    Давно у самого чесались руки самому написать «универсальную» внешнюю компоненту.

    Reply
  6. A_Max

    (1) Там ОООООООЧЕНЬ ограниченный функционал.

    Reply
  7. nixel

    Спасибо огромное!

    Reply
  8. Ershov Mikhail

    Идея хорошая, но поясните один момент:

    Текст для анализа, препологается передавать в виде параметра внешней компоненты.

    например у меня есть лог файл размером 30 Гб. (Txt), правильно ли я понимаю, что перед тем как передать этот текст внешней компоненте, мне необходимо загрузить его в оперативную память ? … а в чём тогда смысл этой компоненты ? ведь одно из основных преимуществ регэкспа — это поточная обработка текста, вне зависимости от размера файла, т.е «Часть прочитал, часть отпустил»… а тут получается сначала загрузи всё в оперативную память а потом запускай свой конвеер Regex … как то «не айс», а текстовым файлом в 100 ГБ я также как и без регекспа имею возможность положить сервер по недостатку памяти. Получается «Кайф» от Regexpa потерян …

    … или я неправ ?

    Reply
  9. KAV2

    (8) В данном случае можно использовать объект ЧтениеТекстаTextReader. Этот объект может читать не весь файл целиком , а построчно.

    Reply
  10. Ershov Mikhail

    (9) т.е. сначала я построчно его читаю 1С-ом, а потом передаю Regex компоненте … но ведь тогда скорость обработки будет примерна такаяже как и без Regexa … узкое место получается чтение 1С … а может сделать возможность передавать компоненте путь к файлу ?

    Reply
  11. KAV2

    (10)Может быть, но пока не готов по этому поводу ответить.

    Reply
  12. azhilichev

    (8) Уважаемый, а как вы иначе хотите? Чтобы компонента вам еще и файл читала? Отделяйте мух от котлет.

    Автор, если эта компонента работает так, как описал в статье, мысленно жму руку!

    Reply
  13. Ershov Mikhail

    (12) Ну если бы компонента имела возможность читать файл, (А если немного обнаглеть то ещё и маску путей как это работает в Bash) был бы вообще огонь … но для начала тоже неплохо, ничего против не имею … это всего лишь идея к развитию проекта …

    Reply
  14. 🅵🅾️🆇

    (3) Не думаю, что стоит под огрызок собирать.

    Такие компоненты лучше подключать на сервере, вряд ли есть извращенцы хостящиеся на macos (да пусть даже и файловые базы).

    Reply
  15. 🅵🅾️🆇

    (10) Тут надо работу с потоками придумывать или разбирать ваш файл построчно/поблоково.

    Вот 2 варианта для работы с текущей компонентой:

    1) воспринимаете ваш файл как ТектовыйДокумент с заданым разделителем и читаете «построчно»

    2) с помощью СтрНайти последовательно выгрызаете кусочки с заданым отступом

    Ну или вы вовсе не правильно воспринимаете задачу, если это некий xml документ, то вам надо, наверное, смотреть в сторону XPath

    Reply
  16. Infactum

    А зачем внутри компоненты все эти конвертации типов для linux, если под капотом все работает вокруг шаблонов std::basic_regex и std::basic_string, которые позволяют задать базовый тип (в случае компоненты WCHAR_T)?

    Reply
  17. Ershov Mikhail

    (15) Задача гепотетчиеская, из серии «Разобрать файл тех журнала» … файл может быть большим, 30-50 ГБ.

    Почему именно она ? потому что чаще приходится пользоваться Regex’ом именно для её решения.

    В Bash — это делается просто, вопрос в том что если есть компонента, которая смогла бы это делать из 1С, минуя Bash — было бы круто и стало бы платформой для тех или иных решений. Но читать текстовый документ построчно и передавать его в ВК с методами Regexa, в рамках этой задачи — скорее всего сильно проиграет Bash’у по скорости и стабильности работы. Как бы исходя из этих мыслей и попросил пояснить возможно ли на текущий момент, используя эту ВК реализовать нечто подобное.

    Reply
  18. 🅵🅾️🆇

    (17) Почему проиграет то?

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

    Если у вас большой файл и его просто надо переварить компонентой, особо не заморачиваясь — делайте регламентной операцией или длительной задачей.

    Если надо срочно и в кратчайшие сроки определенный файл «кушать», то и регэксп и 1с тут не годятся, тк проиграет по скорости специально написанной для этого дела утилите с нормальной асинхронностью и заточенный под поиск определенных паттернов.

    ЗЫ: парсеров технологических журналов — вагон и маленькая тележка

    Reply
  19. Ershov Mikhail

    (18) Возможно я не совсем правильно понял заголовка статьи …

    Там написано : «Внешняя компонента для выполнения регулярных выражений».

    Мои мысли: «Ух ты, круто т.е. нечто типа «grep DEADLOCK */*.log» можно выполнить из 1с ? … было бы прикольно…

    По факту я должен сначала все файлы через оперативку прогнать а потом пользоваться функционалом Regex’a.

    + Читать файл придётся тоже из 1С … что (как мне кажется), будет медленнее чем выполнение этой же процедуры в Bash.

    Хотя технически, ВК, созданная на сервере 1Сб уже имеет все права сервера 1С, вопрос только в описании функционала…

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

    Reply
  20. 🅵🅾️🆇

    (19) Почему быстрее или медленее то? Зачем вам целиком файл пихать в оперативу, вы всегда так делаете?

    Вам надо:

    0) подключить обработчик ожидания

    1) подключить компоненту

    2) в цикле читать файл либо построчно, либо в некий буфер

    3) скармливать компоненте

    4) после завершения цикла отключить компоненту

    Опционально делать это в несколько потоков.

    Оперативы потратите метр-другой.

    Если мы говорим про написание «просмотр файлов в реальном времени», то это задача скорее для реактивной асинхронщины (нужно выводить данные в поток по мере чтения, не забывая выбрасывать из потока информацию находящуюся далеко от указателя пользователя), синхронно переваривать весь файл целиком чтоб показать пользователю кусочек — плохая идея (именно на этом сыпится большинство редакторов и ide, пытающихся синхронно обработать и разметить весь файл до конца).

    Если вам не надо отображать пользователю в реальном времени — то парсите по ночам, как бог пошлет и не парьтесь.

    Reply
  21. kraynev-navi

    (0) Вещь! Не делали сравнений по быстродействию с тем же популярным COMОбъект(«VBScript.RegExp») ?

    Reply
  22. KAV2

    (16) Конвертации лишние и их надо будет убрать. Я оставил это на развитие. Кстати, можно добавлять pull request-ы 😉

    Reply
  23. KAV2

    (21) VBscript быстрее, но там есть ещё пути для оптимизации. Не все оптимально сделано. Принципиально что уже сейчас компонента работает на несколько порядков быстрее чем реализация на 1С в конфигурации 1С:Translator.

    Reply
  24. ayuplotnikov

    Отличная задумка! Успехов вам!

    Подскажите, пожалуйста, как управлять флагами i, g, m?

    Reply
  25. Darklight

    Большое Вам спасибо. Компонента, конечно, замечательная — и я обязательно найду ей применение в своих проектах. А ещё, появление данной публикации, оказалось для меня, как нельзя кстати, я как раз делал свою первую Native компоненты С++ (я совсем не С++ программист) — и уже отчаялся — примеры/шаблоны с ИТС не компилировались, а если заставлял компилироваться — не подключались в 1С. Всё перепробовал. И другие компоненты скачивал — тоже либо не компилировались либо не подключались (на разных платформах и компьютерах и x86 и x64). Думал уже забить на это дело. И тут увидел эту публикацию — и О ЧУДО — она заработала — и на основе шаблона вашей компоненты я сделал свою — и она тоже заработала — АЛИЛУЯ! Но отличия в двух шаблонах так и не нашёл (кроме, разве что не был установлен у меня набор инструментов VS 2017 для XP, использовал набор VS 2013 для XP — но его установка и применение в старом проекте всё-равоно так и не решило проблему), хотя было ещё одно — внешние C++ SDK зависимости (cpp и h) у меня в старом проекте тянулись из каталога SDK версии 7.1, а вашем проекте из SDK версии 8.1 — где и как это настраивается я так и не нашёл (в настройках проекта каталоги в обоих проектах вели именно в SDK версии 7.1). А при применении SDK версии 7.1 с одним из примеров компоненты, скачанной в исходниках из интернета, вообще не компилировался и выдавал ошибку про термы которые «не известны» внутри этого SDK (но мой старый проект, как ни странно, всё-таки компилировался, но компонента не подключалась). В общем, как же это тяжко писать компоненты на VS C++ — спасибо Вам за то, что ваша, вовремя, подоспевшая публикация, помогла мне выбраться из этого тупика! Вот бы ещё разобраться как этот SDK в создаваемых в VS 2017 проектах настроить чтобы знать где же выбирается каталог SDK, откуда должны подтягиваться внешние зависимости (cpp и h).

    Reply
  26. Dmitryiv

    (8) А может не стоит файлы такого размера обрабатывать средствами 1С? Ну совсем не тот инстумент.

    Reply
  27. Ershov Mikhail

    (26) Создатели конфигурации ЦУП и ЦКК из Корпоративного пакета, считают иначе … Они пошли дальше, не только парсят их в 1С, ещё и хранят их в РС … в разобранном виде. На продуктивной БД размером в 1 ТБ, и 200 пользователей в онлайне — Операция «Анализ вызовов кластера» по ТЖ весит 250 Гб , при истории данных в полдня …

    В общем то я их 1Сом и не парсю, для этого используется Bash с Regex’ом. Т.к. это компонента как раз про Regex, думал что может тогда без Bash’a сразу из 1С можно пользоваться Regex’ом … но видимо не сейчас, возможно когда-нибудь прогресс дойдёт и до полномасштабного Regex’а из 1С.

    Reply
  28. zakiap

    Похожа на RexV8 только работает медленнее, но зато есть исходники и работа по linux

    Reply
  29. KAV2

    (24) Честно сказать ни разу не пользовался подобными флагами. Надо будет изучить вопрос.

    Reply
  30. KAV2

    (25) Спасибо большое за теплый отзыв! Кстати компонента, собранная на SDK 8 работает и на XP, по крайней мере с SP3 (кажется в одном из сервис паков расширялся формат исполняемых файлов).

    Reply
  31. KAV2

    (28) Да с производительностью есть куда расти, надеюсь поработать над этим в следующих версиях компоненты.

    Reply
  32. Darklight

    (28)

    (31) Там в осуждении писали про нестабильную работу RexV8. Проверьте у себя регулярки из поста 62 и ещё нескольких более ранних постов от tormozit.

    Reply
  33. KAV2

    (32) Ну на указанных сценариях компонента не падает.

    Reply
  34. KAV2

    (24) В общем, реализация из стандартной библиотеки С++ не поддерживает эти флаги в синтаксисе регулярных выражений. Можно добавить установку этих флагов отдельно, если в них реальная есть потребность, либо за основу взять реализацию не стандартной библиотеки, а скажем boost.

    Reply
  35. ayuplotnikov

    (34) Конкретно для меня критически важен регистронезависимый режим.

    Я сейчас в 1С использую VBScript.RegExp. У него эти параметры задаются через булевы свойства Global, IgnoreCase, Multiline. В std::regex — через аргумент конструктора. Существенной разницы между способами я не вижу.

    Можно добавить необязательные аргументы к существующим методам или добавить метод, который устанавливает флаги. Все варианты обратно совместимы с текущей реализацией.

    Reply
  36. KAV2

    Добавлена в список загрузок отдельным файлом, внешняя компонента, собранная для Mac OS ( тестировалась и собиралась на версии Sierra 10.12).

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

    Подключить компоненту можно с помощью метода ПодключитьВнешнююКомпоненту:

    ПодключитьВнешнююКомпоненту(«<Путь к файлу компоненты>/RegExMac64_4.so», «Component», ТипВнешнейКомпоненты.Native)

    Reply
  37. KAV2

    Новая версия компоненты — 5

    Изменения в версии:

    — переезд на boost::regex (за счет этого повышена стабильность работы)

    — добавлено свойство GlobalВсеСовпадения

    — добавлено свойство IgnoreCaseИгнорироватьРегистр

    — добавлено свойство TemplateШаблон

    — добавлено свойство ErrorDescriptionОписаниеОшибки

    — добавлено свойство ThrowExceptionsВызыватьИсключения

    — добавлена обработка исключений

    — добавлен метод VersionВерсия

    — оптимизирована производительность

    — потеряна совместимость с WindowsXP

    Reply
  38. KAV2

    (24) Выложена новая версия компоненты, где кроме всего прочего добавлены свойства GlobalВсеСовпадения и IgnoreCaseИгнорироватьРегистр.

    Также, реализована возможность задавать модификаторы синтаксиса perl, описанные здесь (синтаксис отличается JavaScript) — https://www.regular-expressions.info/modifiers.html

    Reply
  39. KAV2

    (21) В компоненте версии 5 теперь не все так однозначно со скоростью. Во первых, если вызывается метод один раз, и в него передать большой текст, то компонента теперь может обходить по скорости VBScript.RegExp, но вот если в цикле часто вызывать методы компоненты, передавая каждый раз небольшой текст для анализа, то VBScript.RegExp быстрее. Связано это с тем, что у платформы 1С высокие накладные расходы на вызов методов Native API внешней компоненты. Даже вызов пустого метода отрабатывает медленнее чем вызов метода COM сервера. Во всяком случае на платформах 8.3.12 и 8.3.13. Похоже что разработчик компоненты на это никак повлиять не может.

    Reply
  40. karpik666

    Молодец, этого действительно не хватало, в своей разработке для работы в linux использовал обработку регулярок чисто кодом 1С, штука крутая, но скрость просто убивала.

    Reply
  41. KAV2

    (14) Ну так или иначе, а сборка под Mac OS уже доступна 🙂

    Reply
  42. KAV2

    Новая версия компоненты — 6

    Изменения в версии:

    — Добавлена поддержка наименований методов и свойств без учета рЕгИсТрА.

    — Исправлена ошибка задвоения результатов, если установлен флаг Global под Linux или Mac OS.

    — Исправлена утечка памяти под Linux или Mac OS.

    Reply
  43. KAV2

    Новая версия компоненты — 7

    Изменения в версии:

    — Исправлена утечка памяти при работе под Linux и MacOS

    — Удалена зависимость (статическая, при линковке) от библиотеки iconv при работе под Linux и MacOS

    — Удален лишний программный код и выполнена небольшая оптимизация.

    — Уменьшен размер файла компоненты для MacOS

    Reply
  44. KAV2

    Новая версия компоненты — 8

    Изменения в версии:

    — Исправлены ошибки

    — Оптимизация

    Reply
  45. KAV2

    Новая версия компоненты — 9

    Изменения в версии:

    — Исправлены ошибки

    — Оптимизация

    — Уменьшен размер файлов компоненты

    Reply
  46. KAV2

    Добавлен отдельным файлом тестовый архив внешних компонент для Android.

    Reply
  47. KAV2

    Добавлена отдельным файлом сборка для 64-битной версии Google Chrome под Linux

    Reply
  48. KAV2

    Добавлена сборка для Google Chrome под Windows.

    Reply
  49. Trener

    (28)да, я тоже сразу подумал о RExV8 ,реально очень похоже. Вот только медленно все просто ужас(

    Reply
  50. KAV2

    (49) Относительно низкая скорость работы объясняется высокими накладными расходами на вызов методов внешней компоненты, это особенность работы текущих версий платформы. Если передавать большой объем данных за один вызов, тогда скорость работы должна быть на уровне, по крайнее мере точно быстрее чем VBScript.RegExp.

    Данная компонента работает на несколько порядков быстрее реализации движка на языке 1С, и при этом доступна почти на всех платформах, включая Mac OS и Android.

    Reply
  51. GorDAn

    Компонента при обходе результата (ВсеСовпадения = Истина) пропускает пустые подгруппы. Из за этого непонятно, когда заканчивается одно совпадение и начинается другое. Можно настроить, чтобы выводились все группы?

    Reply
  52. KAV2

    (51) Нет, настроить требуемое поведение в текущей версии нельзя. Если можно, скинье пожалуйста пример шаблона и пример данных к которым этот шаблон применяется.

    Reply
  53. coolo1

    А в возвращаемом значении вы могли бы возвращать еще SubMatches?

    Reply
  54. KAV2

    (53) все найденные группы, включая вложенные возвращаются последовательно.

    Reply
  55. coolo1

    (54)не очень понимаю как этой последовательностью пользоваться… например

    «яблоко красное и тыква черная и еще одно яблоко фиолетовое и еще одно яблоко белое»
    яблоко[s](S+)

    ожидаю что будет 3 найденных:

    яблоко красное

    яблоко фиолетовое

    яблоко белое

    и в каждом вхождении был бы цвет в SubMatches, но если он последовательно выводит то как предполагается использовать группировки?

    В шаблоне искать сколько открытых и закрытых скобочек и после первого вывода отсчитывать их количество, что бы понять когда закончилась группа и началась строка поиска?

    Reply
  56. KAV2

    (55) Этот момент оставил на развитие. Если предложите вариант и удасться его реализовать то постараюсь оперативно тестовую сборку отправить. Ну и опять же это опенсорс, так что всегда можно доработать под свои нужды.

    Более детально можно обсудить в личных сообщениях (или в gitter).

    Как вариант:

    Добавить пустой результат между группами, то есть:

    яблоко красное

    красное

    (пустая строка в результате)

    яблоко фиолетовое

    фиолетовое

    (пустая строка в результате)

    яблоко белое

    белое

    (пустая строка в результате)

    Reply
  57. aleks_p99

    Полезный компонент.

    Спасибо)

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *