Исправление ссылок на законодательство: эпизод 1 (статьи и части)

опубликовано: 26.08.2017

Андрей Костенко

В правовых текстах часто используются ссылки на нормы нормативно-правовых актов, в которых ключевым элементом является указание номеров статей, частей, пунктов, подпунктов, абзацев и так далее. В целом, принято записывать такие ссылки в направлении от частного к общему:
• часть 5 статьи 12 Закона Украины "Об ...";
• пункт 3 части 5 статьи 12 Закона Украины "Об ...";
• подпункт 2 пункта 3 части 5 статьи 12 Закона Украины "Об ...";
• абзац 1 части 4 статьи 17 Закона Украины "Об ...";
и так далее.

Но встречаются документы, в которых их авторы имеют привычку записывать ссылки наоборот, от общего к частному (то есть не "ч. 5 ст. 12", а "ст. 12 ч. 5"). В особенности это касается судебных решений, что немного огорчает. Ведь проблема в том, что при автоматизации анализа подобных документов могут не срабатывать алгоритмы поиска, сбора, подсчёта и других операций с подобными ссылками, которые не учитывают такую "неожиданность".

Например, паттерн регулярного выражения /ч\.(\s| |)\d{1,4}(\s| |)ст\.(\s| |)\d{1,4}/gi, нацеленный на распознание ссылок типа "ч. 5 ст. 12" не заметит ссылку типа "ст. 12 ч. 5". В результате поиск и другие операции в таком тексте с таким паттерном будут неполноценными.

Конечно, можно использовать более сложный паттерн
/(ч|ст)\.(\s| |)\d{1,3}(\s| |)(ст|ч)\.(\s| |)\d{1,4}/gi,
который будет находить и тип "ч. 5 ст. 12", и тип "ст. 12 ч. 5", или использовать последовательно два паттерна для двух разных типов, и успешно находить все ссылки, но всё же может существовать потребность в том, чтобы автоматизировать приведение всех ссылок к правильному типу ДО осуществления их содержательного анализа:

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

перейти сразу к рабочей модели перейти сразу к исходнику кода

Для начала нужно ближе познакомиться с регулярными выражениями (жарг. "регулярками").
Их можно считать швейцарским ножом в сфере программного анализа текстов. Это набор специальных конструкций, которые позволяют создавать паттерны (узоры) – требования к искомому образу текста. Например, с их помощью можно находить только те слова, после которых стоят числа с заданным количеством цифр, проверять корректность телефонных номеров, веб-ссылок, адресов e-mail и так далее. Ниже я упрощённо объясню лишь те элементы паттернов, которые буду использовать при решении задачи этой статьи.

красным цветом обозначены объясняемые элементы

/ / – это границы паттерна, после правой границы ставятся флаги (например, "g", "i");

/ /g – флаг "g" указывает программе, что нужно проводить поиск всех совпадений во всём тексте;

/ /i – флаг "i" указывает программе, что не следует обращать внимание на регистр букв (то есть "А" и "а" будут считаться одинаковыми находками);

/часть/gi – это собственно то, что ищем (в данном случае будут найдены все буквосочетания "часть", "ЧАСТЬ", "ЧаСтЬ" и т.д.);

\ – знак экранирования символов, позволяющий использовать их в другой роли, например:
• "d" – это четвёртая буква английского алфавита, а "\d" – это обозначение любой цифры;
• а вот с точкой наоборот: "." найдёт любой символ (кроме перевода строки), а "\." – собственно точку;

/\d/ – найдёт любую цифру;

/\s/ – найдёт любой пробельный символ;

/./ – найдёт любой символ, кроме перевода строки;

{} – фигурные скобки используются, в частности, для обозначения количества искомых символов (например, паттерн /\d{2,3}/ будет находить все числа, состоящие из двух или трёх цифр, то есть, по сути, от 10 до 999, а также такие сочетания цифр, как 00, 01, 02 … 09 и 000, 001, 002 … 099);

(|) – такая конструкция используется для поиска одного из нескольких вариантов, указанных по разные стороны вертикальной линии (например, паттерн /Евро-(2012|2016)/ будет находить как совпадения "Евро-2012", так и совпадения "Евро-2016").

Вот весь "строительный материал" для нашей регулярки и разобран. Теперь пошагово выстроим нужный паттерн для поиска плохих ссылок типа "ст. 25 ч. 2".

1. //gi – поиск будет глобальным и регистронезависимым.

2. /ст\./gi – ищет буквосочетание "ст", после которого сразу идёт точка.

3. /ст\.(\s| |)/gi – ищет знакосочетание "ст.", после которого идёт одно из трёх: любой пробельный символ, на всякий случай просто пробел (так нужно) либо отсутствие пробелов (бывает и такое, что пробелы пропущены: "ст.4", "ст.110" и т.д.).

4. /ст\.(\s| |)\d{1,4}/gi – к предыдущему добавляется потребность в наличии цифр в количестве от одной до четырёх (например, в Гражданском кодексе Украины сейчас 1308 статей).

5. теперь строим вторую половину паттерна по такому же принципу: буква "ч" + точка + пробелы + цифры = ч\.(\s| |)\d{1,3} .

6. между первой и второй частями паттерна нужно добавить такое же обозначение пробелов или их отсутствия: (\s| |) .

7. соединяем всё это, и вот окончательный результат:

/ст\.(\s| |)\d{1,4}(\s| |)ч\.(\s| |)\d{1,3}/gi

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

/ст\.(\s| |){1,9}\d{1,4}(\s| |){1,9}ч\.(\s| |){1,9}\d{1,3}/gi

То есть теперь он будет находить и такие ссылки, где между любыми элементами может стоять до 9 пробелов! Надеюсь, этого будет достаточно для большинства судебных решений.

Но и этого, на самом деле, еще недостаточно. После букв может быть пропущена точка, например: "ст 8 ч 2", "ст. 8 ч 2", "ст 8 ч. 2". Чтобы "не обжечься" и здесь, нужно еще усилить паттерн:

/ст(\.|)(\s| |){1,9}\d{1,4}(\s| |){1,9}ч(\.|)(\s| |){1,9}\d{1,3}/gi

А также следует защититься от ситуаций, когда точек стоит больше одной, используя метасимвол +, который идентичен конструкции "{1,+бесконечность}":

/ст(\.+|)(\s| |){1,9}\d{1,4}(\s| |){1,9}ч(\.+|)(\s| |){1,9}\d{1,3}/gi

Далее это регулярное выражение становится во главе скрипта, который действует по такому алгоритму:
1) находит в заданном тексте все плохие ссылки и выписывает их в отдельную область памяти;
2) разделяет их на составляющие (для этого также используются регулярные выражения);
3) меняет эти составляющие местами и соединяет их в хорошую (исправленную) ссылку;
4) убирает в исправленных ссылках лишние пробелы и точки (также будет нелишним убрать это и в хороших ссылках);
5) заменяет в заданном тексте все ранее найденные плохие ссылки на хорошие;
6) выводит новый текст.

Но даже такой, казалось бы, мощный паттерн не учитывает таких ситуаций:
1) когда попадается статья с меткой, например: "ст. 81" или "ст. 8-1";
2) когда упоминается несколько частей одной статьи, например: "ст. 5 ч. 1, 2" или "ст. 5 ч.ч. 1, 2";
3) когда вместо цифры "3" стоит заглавная буква "З", что случается при оцифровке текстов.

Как корректно обработать и эти ситуации, будет рассказано в следующей статье.

перейти к рабочей модели перейти к исходнику кода

Нашли ошибку или есть что дополнить?
Пишите на e-mail contact@legaltech.org.ua или сюда.