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