Ни для кого не секрет, что 1С склонять не умеет. Тем не менее, многие пытаются ее заставить это сделать с помощью внешних компонент, сторонних библиотек и написания собственных функций.
Встала и передо мной такая задача. Попытки что-то нагуглить принесли свои плоды. Сначала я пытался найти готовые функции на 1С, но результат работы того что удалось найти никуда не годился - слишком много ошибок - поэтому было решено использовать внешние библиотеки. Первое что я нашел была библиотека Морфер. На сайте есть демонстрация работы библиотеки, есть возможность ее купить, а также есть возможность бесплатного использования веб-сервиса для собственных целей. Это все чудесно, но покупать не хотелось, а передавать названия должностей и подразделений в Интернет - такая себе идея.
Моей радости не было предела, когда я нашел библиотеку padeg.dll, но радость была недолгой, потому что после некоторого времени работы она намертво вешала сервер. Причину выяснить не удалось, поэтому пришлось от нее отказаться. Поиски продолжились.
Коллега шарпист подсказал библиотеку pymorphy2, написанную на python, и это больно, т.к. из скрипта dll не сделаешь. Сделали из него приложение с помощью py2exe, но пользоваться этим было мучительно.
Вся эта история кончилась тем, что мы купили-таки morpher и встроили его в конфигурацию, но руки продолжали чесаться, и я решил сделать локальный сервер склонения, благо, что в python уже встроен CGI-сервер. К нему удобнее обращаться и получать результат, да и небольшое изучение python не повредит.
Итак, потребовалась установка python 3.5 на сервер 1С. Процесс установки рассказывать не буду, это выходит за рамки статьи, да и материала в сети хватает. При установке проследите, чтобы была установлена галочка, добавляющая путь к python в переменную окружения PATH.
Далее, установим pymorphy2. Для этого необходимо запустить командную строку и выполнить следующую команду:
> pip install pymorphy2
Теперь напишем скрипт, запускающий сервер, принимающий параметры и возвращающий результат:
# Подключение необходимых модулей
import pymorphy2
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
# Обработчики запросов
class HttpProcessor(BaseHTTPRequestHandler):
def do_POST(self):
result = list() # Сюда будет записываться результат
# Чтение и разбор параметров
postVars = self.rfile.read(int(self.headers['Content-Length']))
parametrs = json.loads(postVars.decode())
# Параметры - два массива, первый с данными, второй со значениями падежей
data = parametrs.get('data')
case = parametrs.get('case')
# Склонение фраз
i = 0
while i < len(data):
subres = ''
for word in data[i].split():
resword = morph.parse(word)[0]
# Склоняем только то, что стоит в именительном или винительном падеже,
# иначе библиотека все слова просклоняет и получится ерунда
if case[i] == '' or not ('nomn' in resword.tag or 'accs' in resword.tag) or 'plur' in resword.tag:
subres += ' ' + word
else:
# Попытка вернуть слова к тому регистру, в котором они изначально были
if word.isupper():
currentword = resword.inflect({'sing', case[i]}).word.upper()
elif word[0].isupper():
currentword = resword.inflect({'sing', case[i]}).word.capitalize()
else:
currentword = resword.inflect({'sing', case[i]}).word
subres += ' ' + currentword
result.append(subres.lstrip())
i += 1
# Формирование и возврат результата
self.send_response(200)
self.send_header('content-type', 'text/html')
self.end_headers()
self.wfile.write(json.dumps(result, ensure_ascii=False).encode())
# Инициализация pymorphy2
morph = pymorphy2.MorphAnalyzer()
# Инициализация и старт сервера
serv = HTTPServer(('localhost', 8080), HttpProcessor)
serv.serve_forever()
Это всего лишь упрощенный пример, в котором не обрабатываются ситуации передачи некорретных данных в качестве параметров. По коду видно, что сервер слушает порт 8080, и используется метод POST. К нему и будем подключаться. Код подключения:
&НаСервере
Процедура ПросклонятьНаСервере()
Соединение = Новый HTTPСоединение("localhost",8080);
Запрос = Новый HTTPЗапрос("/");
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ПроверятьСтруктуру = Ложь;
ЗаписьJSON.УстановитьСтроку(Новый ПараметрыЗаписиJSON);
ЗаписьJSON.ЗаписатьНачалоОбъекта();
ЗаписьJSON.ЗаписатьИмяСвойства("data");
ЗаписьJSON.ЗаписатьНачалоМассива();
Для Каждого Стр Из ИсходныеДанные Цикл
ЗаписьJSON.ЗаписатьЗначение(Стр.ИсходнаяСтрока);
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьИмяСвойства("case");
ЗаписьJSON.ЗаписатьНачалоМассива();
Для Каждого Стр Из ИсходныеДанные Цикл
ЗаписьJSON.ЗаписатьЗначение(Стр.Падеж);
КонецЦикла;
ЗаписьJSON.ЗаписатьКонецМассива();
ЗаписьJSON.ЗаписатьКонецОбъекта();
Запрос.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), "UTF-8", ИспользованиеByteOrderMark.НеИспользовать);
РезультатЗапросаКСервису = Неопределено;
Попытка
РезультатЗапросаКСервису = Соединение.ОтправитьДляОбработки(Запрос);
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(НСтр("ru='Не удалось выполнить склонение с помощью сервиса..'"));
Для Каждого Стр Из ИсходныеДанные Цикл
Стр.РезультатСклонения = "";
КонецЦикла;
Возврат;
КонецПопытки;
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(РезультатЗапросаКСервису.ПолучитьТелоКакСтроку("UTF-8"));
РезультатЧтения = ПрочитатьJSON(Чтение);
Если ТипЗнч(РезультатЧтения) = Тип("Массив") Тогда
Сч = 0;
Для Каждого Эл Из РезультатЧтения Цикл
ИсходныеДанные[Сч].РезультатСклонения = Эл;
Сч = Сч + 1;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
ИсходныеДанные - таблица значений с колонками: ИсходнаяСтрока, Падеж, РезультатСклонения. Возможные варианты падежей смотри в документации:
nomn | именительный | Кто? Что? | хомяк ест |
gent | родительный | Кого? Чего? | у нас нет хомяка |
datv | дательный | Кому? Чему? | сказать хомяку спасибо |
accs | винительный | Кого? Что? | хомяк читает книгу |
ablt | творительный | Кем? Чем? | зерно съедено хомяком |
loct | предложный | О ком? О чём? и т.п. | хомяка несут в корзинке |
voct | звательный | Его формы используются при обращении к человеку. | Саш, пойдем в кино. |
gen2 | второй родительный (частичный) | ложка сахару (gent - производство сахара); стакан яду (gent - нет яда) | |
acc2 | второй винительный | записался в солдаты | |
loc2 | второй предложный (местный) | я у него в долгу (loct - напоминать о долге); висит в шкафу (loct - монолог о шкафе); весь в снегу (loct - писать о снеге) |
Следующий шаг - запуск сервера командой:
> py "<Путь к файлу>\morph.py"
Предванительно необходимо добавить путь к интерпретатору python в переменную окружения PATH, если это не было сделано при установке, и сохранить текст скрипта в файл 'morph.py' на диск.
Все, теперь можно обращаться к сервису и смотреть результат.
При использовании сервиса в печатных формах идея алгоритма следующая:
- Получить из системы данные для заполнения печатной формы.
- Сформировать массив из тех строк, которые необходимо просклонять.
- Сформировать массив такой же длины с указанием падежей, к которым необходимо привести строки.
- Преобразовать массивы в JSON вида:
{ "data": ["str1",...,"strN"], "case": ["case1",...,"caseN"] }
- Отправить полученную строку на сервер.
- Получить и обработать результат работы сервера (массив в формате JSON)
При таком подходе для одной печатной формы будет всего одно обращение к серверу, что сократит время её формирования. Порядок строк в результирующем массиве такой же, как в исходном.
Была написана небольшая обработка, демонстрирующая работу с сервисом, которую приложил в файлы, как и скрипт (жаль, не получается выложить их бесплатно, буду благодарен, если кто-нибудь подскажет как это сделать). Проверял ее в ERP 2. Возможно, заработает в ЗУП 3.0. Если не использовать автоазполнение, то будет работать в типовых конфигурациях на БСП. Результат работы обработки на скриншоте в начале статьи.
На этом все. Хочется сказать огромное спасибо автору библиотеки pymorphy2. Это мощнейший инструмент, на который автор не пожалел своего собственного времени и сил, и который теперь обычные смертные могут использовать совершенно безвозмездно.