Когда мы работаем со строковыми данными в 1С, часто возникает потребность найти не просто последовательность символов, а именно целое слово. Стандартная функция СтрНайти() отлично справляется с поиском подстрок, но не умеет учитывать границы слов. Например, если мы ищем "123" в строке "№123456", СтрНайти() найдет ее, но "123" в данном случае не является самостоятельным словом. Давайте разберем, как можно реализовать такой функционал в 1С, используя различные подходы, подходящие как для старых, так и для современных версий платформы.
Мы рассмотрим несколько основных методов реализации поиска слова целиком:
СтрНайти().Этот подход является наиболее универсальным и подходит для всех версий платформы 1С, так как он базируется на стандартной функции СтрНайти(). Суть метода заключается в том, что после того как СтрНайти() обнаружит искомую подстроку, мы дополнительно проверяем символы, расположенные непосредственно до и после этой подстроки. Если эти символы являются разделителями слов (пробелы, знаки препинания, начало или конец строки), то мы считаем, что нашли целое слово.
Рассмотрим пошаговый алгоритм, как мы можем реализовать такую проверку:
СтрНайти(<ИсходнаяСтрока>, <ИскомаяПодстрока>, <НаправлениеПоиска>, <НачальнаяПозиция>) для поиска первого вхождения искомой подстроки. Функция возвращает позицию первого символа найденной подстроки (нумерация с 1) или 0, если подстрока не найдена.N не равна 0), нам необходимо проверить символы вокруг нее.N равно 1 (подстрока находится в самом начале строки), то перед ней нет символов, и это можно считать границей слова.N больше 1, мы получаем символ по позиции N - 1 с помощью функции Сред(<ИсходнаяСтрока>, N - 1, 1). Этот символ должен быть разделителем (пробел, знак препинания).ПозицияОкончания = N + СтрДлина(<ИскомаяПодстрока>) - 1.ПозицияОкончания равна СтрДлина(<ИсходнаяСтрока>) (подстрока находится в самом конце строки), то после нее нет символов, и это можно считать границей слова.ПозицияОкончания меньше СтрДлина(<ИсходнаяСтрока>), мы получаем символ по позиции ПозицияОкончания + 1. Этот символ также должен быть разделителем.N + СтрДлина(<ИскомаяПодстрока>) (или N + СтрДлина(<ИскомаяПодстрока>) + 1, если мы хотим перескочить текущую невалидную находку), пока не найдем подходящее вхождение или не дойдем до конца строки.Давайте посмотрим на пример кода, который реализует описанный алгоритм. Мы создадим функцию, которая будет возвращать позицию первого найденного слова целиком.
// Функция для проверки, является ли символ разделителем слова
// Мы можем расширить этот список символов по мере необходимости
Функция СимволЯвляетсяРазделителем(Символ)
Разделители = " .,;:-_!?/\|()[]{}<>" + Символ(9) + Символ(10) + Символ(13); // Пробел, знаки препинания, табуляция, перевод строки
Возврат СтрНайти(Разделители, Символ) > 0;
КонецФункции
// Функция для поиска слова целиком
// Возвращает позицию начала слова или 0, если слово не найдено
Функция СтрНайтиСловоЦеликом(ИсходнаяСтрока, ИскомоеСлово)
НачальнаяПозицияПоиска = 1;
ДлинаИсходнойСтроки = СтрДлина(ИсходнаяСтрока);
ДлинаИскомогоСлова = СтрДлина(ИскомоеСлово);
Пока Истина Цини
ПозицияВхождения = СтрНайти(ИсходнаяСтрока, ИскомоеСлово, , НачальнаяПозицияПоиска);
Если ПозицияВхождения = 0 Тогда
// Слово не найдено или больше нет вхождений
Возврат 0;
КонецЕсли;
// Проверяем символ до слова
ГраницаСлева = Ложь;
Если ПозицияВхождения = 1 Тогда
// Слово в начале строки - граница слева есть
ГраницаСлева = Истина;
Иначе
// Проверяем предыдущий символ
СимволПеред = Сред(ИсходнаяСтрока, ПозицияВхождения - 1, 1);
Если СимволЯвляетсяРазделителем(СимволПеред) Тогда
ГраницаСлева = Истина;
КонецЕсли;
КонецЕсли;
// Проверяем символ после слова
ГраницаСправа = Ложь;
ПозицияОкончанияСлова = ПозицияВхождения + ДлинаИскомогоСлова - 1;
Если ПозицияОкончанияСлова = ДлинаИсходнойСтроки Тогда
// Слово в конце строки - граница справа есть
ГраницаСправа = Истина;
Иначе
// Проверяем следующий символ
СимволПосле = Сред(ИсходнаяСтрока, ПозицияОкончанияСлова + 1, 1);
Если СимволЯвляетсяРазделителем(СимволПосле) Тогда
ГраницаСправа = Истина;
КонецЕсли;
КонецЕсли;
Если ГраницаСлева И ГраницаСправа Тогда
// Нашли слово целиком
Возврат ПозицияВхождения;
Иначе
// Это не целое слово, продолжаем поиск с новой позиции
НачальнаяПозицияПоиска = ПозицияОкончанияСлова + 1;
Если НачальнаяПозицияПоиска > ДлинаИсходнойСтроки Тогда
// Больше нет места для поиска
Возврат 0;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецФункции
// Примеры использования:
// Строка 1: "Это тестовая строка с тестовыми данными."
// Строка 2: "Ищем слово 'тест', но не 'тестовая'."
// Строка 3: "Слово в начале. Слово в конце."
// Строка 4: "Слово, окруженное знаками препинания."
// Строка 5: "123456789"
// Строка 6: "№123456"
// Пример 1: Слово "тестовая"
Позиция = СтрНайтиСловоЦеликом("Это тестовая строка с тестовыми данными.", "тестовая"); // Должно найти
Сообщить("Поиск 'тестовая' в 'Это тестовая строка': " + Позиция); // Выведет 5
// Пример 2: Слово "тест"
Позиция = СтрНайтиСловоЦеликом("Это тестовая строка с тестовыми данными.", "тест"); // Не должно найти как отдельное слово
Сообщить("Поиск 'тест' в 'Это тестовая строка': " + Позиция); // Выведет 0
// Пример 3: Слово "слово" в разных позициях
Позиция = СтрНайтиСловоЦеликом("Слово в начале. Слово в конце. Слов.", "Слово"); // Должно найти первое "Слово"
Сообщить("Поиск 'Слово' в 'Слово в начале. Слово в конце. Слов.': " + Позиция); // Выведет 1
// Пример 4: Слово с номером
Позиция = СтрНайтиСловоЦеликом("№123456", "123"); // Не должно найти
Сообщить("Поиск '123' в '№123456': " + Позиция); // Выведет 0
Важно: При таком подходе мы должны учесть регистр. Если требуется поиск без учета регистра, следует предварительно привести обе строки к одному регистру (например, с помощью НРег() или ВРег()) перед передачей их в функцию СтрНайтиСловоЦеликом().
С версии 8.3.23 платформы "1С:Предприятие" появилась нативная поддержка регулярных выражений, что значительно упрощает и расширяет возможности работы со строками. Регулярные выражения — это мощный инструмент для поиска и обработки текстовых данных по шаблону, и они идеально подходят для задачи поиска слова целиком.
\b для границ словаВ регулярных выражениях существует специальный метасимвол \b (word boundary), который обозначает границу слова. Граница слова определяется как позиция между символом слова (буква, цифра, знак подчеркивания) и символом не-слова, или между символом слова и началом/концом строки. Например, для поиска слова "sun" целиком, но не "sunny" или "sunshine", мы будем использовать регулярное выражение \bsun\b.
В платформе 1С для работы с регулярными выражениями используются следующие функции:
СтрНайтиПоРегулярномуВыражению(<Строка>, <РегулярноеВыражение>, <НаправлениеПоиска>, <НачальнаяПозиция>, <НомерВхождения>): Эта функция находит в строке вхождения подстрок, соответствующих заданному регулярному выражению. Она возвращает объект РезультатПоискаПоРегулярномуВыражению, который содержит информацию о найденной подстроке (позиция, длина).СтрПодобнаПоРегулярномуВыражению(<Строка>, <РегулярноеВыражение>): Проверяет, соответствует ли строка целиком заданному регулярному выражению.СтрЗаменитьПоРегулярномуВыражению(<Строка>, <РегулярноеВыражение>, <ПодстрокаЗамены>): Находит все вхождения, соответствующие регулярному выражению, и заменяет их.Для нашей задачи поиска слова целиком наиболее подходит функция СтрНайтиПоРегулярномуВыражению().
Рассмотрим, как мы можем использовать регулярные выражения для поиска слова "тест" целиком.
// Функция для поиска слова целиком с использованием регулярных выражений
// Возвращает позицию начала слова или 0, если слово не найдено
Функция СтрНайтиСловоЦеликомРегВыр(ИсходнаяСтрока, ИскомоеСлово)
РегулярноеВыражение = "\b" + ИскомоеСлово + "\b"; // Создаем регулярное выражение с границами слова
// Выполняем поиск
Результат = СтрНайтиПоРегулярномуВыражению(ИсходнаяСтрока, РегулярноеВыражение);
Если Результат.Найдено Тогда
Возврат Результат.Позиция; // Возвращаем позицию найденного слова
Иначе
Возврат 0; // Слово не найдено
КонецЕсли;
КонецФункции
// Примеры использования:
// Строка 1: "Это тестовая строка с тестовыми данными."
// Строка 2: "Ищем слово 'тест', но не 'тестовая'."
// Строка 3: "Слово в начале. Слово в конце."
// Строка 4: "Слово, окруженное знаками препинания."
// Строка 5: "123456789"
// Строка 6: "№123456"
// Пример 1: Слово "тестовая"
Позиция = СтрНайтиСловоЦеликомРегВыр("Это тестовая строка с тестовыми данными.", "тестовая"); // Должно найти
Сообщить("РегВыр Поиск 'тестовая' в 'Это тестовая строка': " + Позиция); // Выведет 5
// Пример 2: Слово "тест"
Позиция = СтрНайтиСловоЦеликомРегВыр("Это тестовая строка с тестовыми данными.", "тест"); // Не должно найти как отдельное слово
Сообщить("РегВыр Поиск 'тест' в 'Это тестовая строка': " + Позиция); // Выведет 0
// Пример 3: Слово "Слово" в разных позициях
Позиция = СтрНайтиСловоЦеликомРегВыр("Слово в начале. Слово в конце. Слов.", "Слово"); // Должно найти первое "Слово"
Сообщить("РегВыр Поиск 'Слово' в 'Слово в начале. Слово в конце. Слов.': " + Позиция); // Выведет 1
// Пример 4: Слово с номером
Позиция = СтрНайтиСловоЦеликомРегВыр("№123456", "123"); // Не должно найти
Сообщить("РегВыр Поиск '123' в '№123456': " + Позиция); // Выведет 0
// Пример 5: Поиск слова с учетом регистра
Позиция = СтрНайтиСловоЦеликомРегВыр("Привет мир! Приветствую всех.", "привет"); // Не найдет, если "Привет" с большой буквы
Сообщить("РегВыр Поиск 'привет' в 'Привет мир!': " + Позиция); // Выведет 0
// Для поиска без учета регистра можно использовать модификатор i (case-insensitive)
// Но в 1С это делается через флаги, если бы они были напрямую доступны в РегулярномВыражении
// Либо можно привести обе строки к нижнему регистру перед поиском, как в первом способе.
// Например:
Позиция = СтрНайтиПоРегулярномуВыражению("Привет мир! Приветствую всех.".НРег(), "\b" + "привет" + "\b");
Если Позиция.Найдено Тогда
Сообщить("РегВыр (НРег) Поиск 'привет' в 'Привет мир!': " + Позиция.Позиция); // Выведет 1
КонецЕсли;
Ограничения: Регулярные выражения в 1С основаны на диалекте ICU, применяемом в языке Perl. В текущей реализации (на момент версии 8.3.23) отсутствует прямая поддержка подвыражений (Submatches) в виде коллекции. Если мы хотим получить все вхождения, нам придется вызывать функцию поиска в цикле, каждый раз указывая новую начальную позицию поиска.
В самой платформе 1С:Предприятие уже существуют мощные механизмы полнотекстового поиска, которые могут учитывать поиск слова целиком и поддерживают различные операторы. Если задача связана не с программным анализом произвольной строки, а с поиском данных в информационной базе или в тексте модулей, эти инструменты могут быть очень полезны.
*, использовать логические операторы (И, ИЛИ, НЕ), а для поиска слов целиком эти механизмы обычно подразумевают, что искомое слово не является частью другого слова. Настройки полнотекстового поиска в конфигурациях позволяют индексировать данные и проводить сложный поиск по содержимому полей и присоединенных файлов.Эти встроенные механизмы, хотя и не являются функцией встроенного языка для программного использования в произвольных строках, предоставляют мощный функционал для пользователей и разработчиков в повседневной работе с системой.
Как было упомянуто в одном из сообщений на форуме, могут существовать внешние библиотеки или подсистемы, которые уже содержат готовую функциональность для поиска слова целиком. Примером может служить некая функция ирОбщий.СтрНайтиСловоЛкс, которая, вероятно, является частью какой-то библиотеки или общего модуля в специфической конфигурации. Использование таких готовых решений может значительно ускорить разработку, поскольку не требует написания и отладки собственного кода.
Если мы работаем в среде, где уже интегрированы такие библиотеки (например, Инфостарт или другие сообщества разработчиков), всегда стоит проверить их наличие и документацию. В них могут быть реализованы как алгоритмы ручной проверки, так и обертки для регулярных выражений, предлагающие более удобный интерфейс.
Мы рассмотрели несколько подходов к решению задачи поиска слова целиком в 1С. Выбор конкретного метода зависит от версии платформы, сложности требуемого поиска и необходимости переносимости решения:
Внимательно проанализировав свои требования и возможности, вы сможете выбрать наиболее подходящий способ реализации поиска слова целиком, значительно улучшив функциональность ваших разработок в 1С.