В практике разработчика 1С часто возникают задачи, связанные с анализом текстовой информации: от простых проверок заполнения до сложных алгоритмов поиска дублей или анализа частотности слов. Одной из базовых, но важных задач является подсчет уникальных слов в произвольном тексте. Разберем подробно, как реализовать этот функционал наиболее эффективно, используя встроенные инструменты платформы 1С:Предприятие 8.3.
Прежде чем переходить к написанию кода, определимся с инструментарием. Нам необходимо создать внешнюю обработку, которая будет принимать текст от пользователя и выдавать результат. Для этого проанализируем необходимые объекты формы:
Текст с типом Строка. В свойствах элемента формы обязательно установим флаг Многострочный режим, чтобы пользователь мог вставлять большие фрагменты текста с переносами строк.Подсчитать и разместим ее на форме в виде кнопки.Рассмотрим наиболее производительный способ решения задачи, предложенный в обсуждении. Использование объекта Соответствие (Map) идеально подходит для поиска уникальных значений, так как поиск по ключу в этой коллекции выполняется крайне быстро благодаря механизмам хеширования. В отличие от Структуры, в Соответствии ключом может быть строка любого вида, включая строки с пробелами или цифрами в начале.
Разберем процесс выполнения по шагам:
СтрРазделить, которая превратит текст в массив подстрок.Соответствие.Проанализируем программный код реализации:
&НаКлиенте
Процедура Подсчитать(Команда)
// Приводим текст к нижнему регистру один раз для оптимизации
ТекстНижнийРегистр = НРег(Объект.Текст);
// Определяем разделители (пробел, знаки препинания, переносы строк)
Разделители = " ,.!?;:-()""";
// Разделяем строку в массив.
// Ложь в третьем параметре исключает пустые строки из результата
МассивСлов = СтрРазделить(ТекстНижнийРегистр, Разделители, Ложь);
// Используем Соответствие для хранения уникальных ключей
УникальныеСлова = Новый Соответствие;
Для Каждого Слово Из МассивСлов Цикл
// Метод Вставить просто перезапишет значение, если ключ уже есть.
// Количество элементов в Соответствии увеличится только для новых слов.
УникальныеСлова.Вставить(Слово, Истина);
КонецЦикла;
КоличествоУникальных = УникальныеСлова.Количество();
Сообщить(СтрШаблон("Количество уникальных слов в тексте: %1", КоличествоУникальных));
КонецПроцедуры
Рассмотрим подробнее технические нюансы этого решения. Функция СтрРазделить появилась в платформе 8.3.6 и работает значительно быстрее, чем старые алгоритмы ручного перебора символов строки в цикле. Важным моментом является третий параметр ВключатьПустые. Если в тексте встречаются два пробела подряд, при значении Истина функция вернет пустую строку в массиве, что исказит наш результат. Установка значения Ложь избавляет нас от лишних проверок Если ЗначениеЗаполнено(Слово) внутри цикла.
Выбор Соответствия обусловлен тем, что при добавлении элемента методом Вставить система сама проверяет наличие ключа. Если слово уже встречалось, оно просто обновит значение по этому ключу, не создавая новой записи. В итоге УникальныеСлова.Количество() вернет именно число неповторяющихся слов.
В ходе дискуссии на форуме возник спор: где лучше вызывать функцию НРег()? Выясним причину, по которой вариант из нашего примера (вызов перед циклом) предпочтительнее. Если мы будем вызывать НРег(Слово) внутри цикла, то функция будет выполнена столько раз, сколько слов в тексте (например, 10 000 раз). Если же мы вызовем ее один раз для всей строки, это значительно сэкономит ресурсы процессора, так как вызов программного интерфейса встроенного языка — это относительно "дорогая" операция.
Некоторые разработчики предпочитают использовать ТаблицуЗначений. Рассмотрим этот способ, так как он может быть полезен, если в дальнейшем вам нужно не только посчитать количество, но и, например, отсортировать слова по алфавиту или вывести их в отчет.
Посмотрим на пример кода:
// Создаем таблицу
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Слово");
// Заполняем таблицу из массива
Для Каждого Слово Из МассивСлов Цикл
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Слово = Слово;
КонецЦикла;
// Сворачиваем таблицу по колонке Слово
ТЗ.Свернуть("Слово");
// Результат - количество строк после сворачивания
КоличествоУникальных = ТЗ.Количество();
Этот метод кажется наглядным, однако стоит учитывать, что создание объекта ТаблицаЗначений и выполнение метода Свернуть требует больше оперативной памяти и времени, чем работа с Соответствием. Кроме того, ТаблицаЗначений доступна только на стороне сервера, что потребует лишнего контекстного вызова, если обработка текста происходит на клиенте.
Проанализируем ситуацию: если пользователь введет текст "Привет, мир! Привет мир.", то при разделении только по пробелу мы получим слова "Привет,", "мир!", "Привет", "мир.". Для программы это четыре разных слова. Чтобы решение было качественным, в переменную Разделители функции СтрРазделить необходимо включить все возможные символы, которые не являются буквами.
Важный совет: В 1С СтрРазделить воспринимает каждый символ в строке разделителей как отдельный разделитель. Поэтому строка " ,.!?;:" корректно обработает большинство текстов. Для более сложного и гибкого выделения слов и разделителей по шаблонам подойдёт подсистема для работы с регулярными выражениями в 1С.
Подводя итог, можно сказать, что связка НРег() + СтрРазделить() + Соответствие является "золотым стандартом" для подобных задач в 1С благодаря своей лаконичности и высокой скорости работы. При необходимости можно использовать готовую обработку для подсчета количества слов в тексте и их вывода.