В стандартной платформе «1С:Предприятие» существует мощная встроенная функция ЧислоПрописью, которая позволяет легко превратить число 1380.00 в строку «Одна тысяча триста восемьдесят рублей 00 копеек». Однако разработчики часто сталкиваются с обратной задачей: когда из текстового файла, распознанного скана или PDF-выписки нужно извлечь сумму, представленную словами, и превратить ее в обычное число для записи в информационную базу.
Рассмотрим подробнее, почему штатных средств для этого не предусмотрено, и разберем по шагам, как написать универсальный алгоритм для решения этой задачи.
Проанализируем архитектуру платформы. В системе 1С первичным всегда является число. Пропись используется исключительно для визуализации данных в печатных формах (счетах, актах, договорах). Поскольку предполагается, что данные в систему вводятся в цифровом виде, обратная задача в типовых конфигурациях встречается крайне редко. Как правило, такая потребность возникает при интеграции с внешними системами или при парсинге документов, не имеющих структурированного формата обмена (например, PDF).
Важно понимать, что ни в платформе, ни в «Библиотеке стандартных подсистем» (БСП) готового метода не существует. Поэтому нам необходимо реализовать собственный алгоритм «накопителя».
Основная сложность преобразования заключается в том, что числительные в русском языке имеют сложную иерархию. Мы не можем просто складывать значения слов. Например, в фразе «две тысячи» слово «две» (2) должно умножиться на «тысячи» (1000), а в фразе «двадцать две» — сложиться.
Проанализируем ситуацию и выделим основные этапы обработки строки:
СтрРазделить.Рассмотрим пример кода, который эффективно справляется с преобразованием. Этот алгоритм умеет обрабатывать рубли, копейки, тысячи, миллионы и миллиарды.
// Функция преобразует прописное число в цифровое
// Пример: "Сто Двадцать Три тысячи Двести Сорок один рубль 15 копеек" -> 123241.15
Функция ПрописьВЧисло(Знач Пропись) Экспорт
// Приводим к нижнему регистру для удобства сравнения
Пропись = НРег(Пропись);
// Инициализируем переменные
ТекущийРазряд = 0;
Результат = 0;
Копейки = 0;
ОбрабатываемКопейки = Ложь;
// Разбиваем строку на слова
Слова = СтрРазделить(Пропись, " ", Ложь);
// Основной цикл по словам
Для Каждого Слово Из Слова Цикл
Если Слово = "" Тогда
Продолжить;
КонецЕсли;
// Определяем момент перехода к копейкам
Если Слово = "копейка" Или Слово = "копеек" Или Слово = "копейки" Тогда
ОбрабатываемКопейки = Истина;
Продолжить;
КонецЕсли;
// Валюту (рубли) просто пропускаем, они служат разделителем
Если Слово = "рубль" Или Слово = "рублей" Или Слово = "рубля" Тогда
Продолжить;
КонецЕсли;
Если НЕ ОбрабатываемКопейки Тогда
// Обрабатываем основную часть (рубли)
СтруктураРезультата = ОбработатьСловоРубли(Слово, ТекущийРазряд, Результат);
ТекущийРазряд = СтруктураРезультата.ТекущийРазряд;
Результат = СтруктураРезультата.Результат;
Иначе
// Обрабатываем дробную часть (копейки)
Копейки = ОбработатьСловоКопейки(Слово, Копейки);
КонецЕсли;
КонецЦикла;
// Добавляем последний накопленный разряд к результату
Результат = Результат + ТекущийРазряд;
// Добавляем копейки к результату
Если Копейки > 0 Тогда
Результат = Результат + Копейки / 100;
КонецЕсли;
Возврат Результат;
КонецФункции
Выясним причину, по которой код разделен на мелкие функции. Это сделано для удобства поддержки: если нам понадобится добавить поддержку триллионов или другой валюты, мы просто расширим соответствующие словари. Рассмотрим логику обработки разрядов:
Функция ОбработатьСловоРубли(Слово, ТекущийРазряд, Результат)
СтруктураРезультата = Новый Структура("ТекущийРазряд, Результат", ТекущийРазряд, Результат);
// 1. Проверяем простые числа (1-9, 10-19, 20-90, 100-900)
Значение = ПолучитьЧисловоеЗначение(Слово);
Если Значение > 0 Тогда
СтруктураРезультата.ТекущийРазряд = ТекущийРазряд + Значение;
Возврат СтруктураРезультата;
КонецЕсли;
// 2. Обрабатываем слова-множители (разряды)
Если Слово = "тысяч" Или Слово = "тысяча" Или Слово = "тысячи" Тогда
СтруктураРезультата.Результат = Результат + ТекущийРазряд * 1000;
СтруктураРезультата.ТекущийРазряд = 0;
ИначеЕсли Слово = "миллион" Или Слово = "миллиона" Или Слово = "миллионов" Тогда
СтруктураРезультата.Результат = Результат + ТекущийРазряд * 1000000;
СтруктураРезультата.ТекущийРазряд = 0;
ИначеЕсли Слово = "миллиард" Или Слово = "миллиарда" Или Слово = "миллиардов" Тогда
СтруктураРезультата.Результат = Результат + ТекущийРазряд * 1000000000;
СтруктураРезультата.ТекущийРазряд = 0;
КонецЕсли;
Возврат СтруктураРезультата;
КонецФункции
Обратите внимание на логику работы с множителями: когда мы встречаем слово «тысяча», мы берем всё, что накопили в ТекущийРазряд, умножаем на 1000 и прибавляем к общему Результату. После этого ТекущийРазряд обнуляется, чтобы начать собирать сотни и десятки следующего порядка.
Для корректного сопоставления слов нам необходимо создать справочник всех возможных вариантов написания чисел. В коде 1С это удобнее всего реализовать через серию условий ИначеЕсли или используя объект Соответствие.
Функция ПолучитьЧисловоеЗначение(Слово)
// Единицы
Если Слово = "один" Или Слово = "одна" Тогда Возврат 1;
ИначеЕсли Слово = "два" Или Слово = "две" Тогда Возврат 2;
// ... и так далее до девяти
// Десятки
ИначеЕсли Слово = "десять" Тогда Возврат 10;
ИначеЕсли Слово = "одиннадцать" Тогда Возврат 11;
// ... и так далее до девятнадцати
// Десятки (20-90)
ИначеЕсли Слово = "двадцать" Тогда Возврат 20;
ИначеЕсли Слово = "тридцать" Тогда Возврат 30;
// Сотни (100-900)
ИначеЕсли Слово = "сто" Тогда Возврат 100;
ИначеЕсли Слово = "двести" Тогда Возврат 200;
Возврат 0;
КонецФункции
Проанализируем ситуацию, когда текст получен путем распознавания сканированного документа. Часто возникают опечатки: например, «одинадцать» вместо «одиннадцать». Обычное строковое сравнение в этом случае вернет 0, и итоговая сумма будет неверной.
Для решения этой проблемы в профессиональных разработках рекомендуется:
VBScript.RegExp для более гибкого поиска слов, игнорируя окончания.Посмотрим на примеры записи дробной части. Копейки могут быть записаны:
Чтобы алгоритм был универсальным, перед обработкой слова функцией ПолучитьЧисловоеЗначение, стоит попытаться преобразовать его в число напрямую с помощью Число(Слово) внутри блока Попытка...Исключение. Если преобразование удалось — значит, копейки указаны цифрами, и мы можем сразу добавить их к результату.
Важный момент: всегда очищайте входную строку от знаков препинания (запятых, точек в середине текста), так как они часто встречаются в банковских выписках для визуального разделения разрядов, но мешают работе функции СтрРазделить.
Используя предложенный подход, мы можем создать надежный инструмент для автоматизации ввода данных, который сэкономит время бухгалтерам и операторам при обработке первичной документации.