К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как найти часть строки в 1С

Как найти часть строки в 1С: функции СтрНайти, Найти, СтрЧислоВхождений, поиск с учётом регистра, примеры кода, типичные ошибки и ответы на вопросы.

Поиск подстроки — одна из самых частых задач при разработке на встроенном языке 1С: проверка, содержит ли наименование нужное слово, разбор номенклатурного номера, поиск фрагмента в адресе или комментарии. Во встроенном языке для этого есть несколько функций, и у каждой свои особенности: направление поиска, учёт регистра, возвращаемое значение.

Основной инструмент современных конфигураций — функция СтрНайти(), появившаяся в платформе 8.3.6 вместе со «строчными» функциями. В старом коде встречается устаревшая Найти(), которая работает иначе и часто становится источником ошибок при доработке типовых решений. Важно понимать разницу между ними, чтобы не получить неожиданный результат.

Ниже разобраны основные способы поиска части строки: позиция вхождения, проверка факта вхождения, подсчёт количества вхождений, поиск без учёта регистра и извлечение найденного фрагмента. Все примеры применимы как в толстом, так и в тонком клиенте, на сервере и в модулях форм.

Функция СтрНайти: базовый синтаксис и параметры

Функция СтрНайти() имеет следующий синтаксис: СтрНайти(Строка, ПодстрокаПоиска, НаправлениеПоиска, НачальнаяПозиция, НомерВхождения). Обязательны только первые два параметра, остальные позволяют тонко управлять поиском.

Результат — число: позиция символа, с которого начинается вхождение (нумерация с единицы), либо 0, если подстрока не найдена. Пустая искомая подстрока считается найденной и возвращает начальную позицию поиска — это важно учитывать при проверках.

Строка = "Заказ покупателя №000123 от поставщика";

Позиция = СтрНайти(Строка, "№000123");

// Позиция = 17

Если СтрНайти(Строка, "поставщик") > 0 Тогда

Сообщить("Подстрока найдена");

КонецЕсли;

Параметр НачальнаяПозиция задаёт, с какого символа начинать поиск, а НомерВхождения — какое по счёту вхождение вернуть. Это удобно, когда нужно найти второе или третье вхождение разделителя, например второй пробел в ФИО.

Поиск с конца строки и поиск нескольких вхождений

Третий параметр — НаправлениеПоиска — принимает значения НаправлениеПоиска.СНачала (по умолчанию) или НаправлениеПоиска.СКонца. Поиск с конца полезен, когда нужно найти последний разделитель: например, последний слэш в пути к файлу, чтобы отделить имя файла от каталога.

Путь = "C:\Документы\Отчеты\Продажи.xlsx";

Позиция = СтрНайти(Путь, "\", НаправлениеПоиска.СКонца);

ИмяФайла = Сред(Путь, Позиция + 1);

// ИмяФайла = "Продажи.xlsx"

Для перебора всех вхождений подстроки используют цикл с параметром НачальнаяПозиция: каждая следующая итерация начинается с позиции после найденного вхождения. Альтернатива — функция СтрЧислоВхождений(), которая сразу возвращает количество вхождений без указания позиций.

Строка = "яблоко;груша;яблоко;слива;яблоко";

Количество = СтрЧислоВхождений(Строка, "яблоко");

// Количество = 3

Учёт регистра: почему поиск «не находит» очевидное

И СтрНайти(), и устаревшая Найти() по умолчанию чувствительны к регистру: «Заказ» и «заказ» — разные подстроки. Это самая частая причина жалоб на «неработающий» поиск. Если регистр не важен, приведите обе строки к одному регистру перед поиском.

Наименование = "Кабель ВВГнг 3х2.5";

Если СтрНайти(НРег(Наименование), НРег("ввгнг")) > 0 Тогда

// вхождение найдено независимо от регистра

КонецЕсли;

Учтите, что НРег() и ВРег() преобразуют только буквы, поэтому такой приём безопасен для цифр и спецсимволов. Однако не используйте результат преобразования для вывода пользователю — только для сравнения, иначе потеряете исходное написание.

Типичные ошибки
01
Поиск без приведения регистра: подстрока 'заказ' не находится в строке 'Заказ покупателя', и проверка ошибочно считается неуспешной
02
Проверка результата через 'Если СтрНайти(...) Тогда': при позиции 0 условие сработает как Ложь, но при любом другом числе — как Истина, что маскирует ошибку; надёжнее явное сравнение '> 0'
03
Использование устаревшей Найти() в новом коде: функция поддерживается для совместимости, но синтакс-помощник и проверка конфигурации помечают её как устаревшую
04
Путаница между СтрНайти() и Найти(): у Найти() параметры идут в порядке (ЧтоИщем, ГдеИщем), а у СтрНайти() — (ГдеИщем, ЧтоИщем); при замене одной функции на другую аргументы нужно поменять местами

Устаревшая функция Найти и отличия от СтрНайти

В конфигурациях, написанных до платформы 8.3.6, повсеместно используется Найти(ПодстрокаПоиска, Строка). Обратите внимание на порядок аргументов — он обратный по сравнению со СтрНайти(). Это главный подводный камень при рефакторинге: механическая замена имени функции без перестановки параметров приводит к тому, что поиск всегда возвращает 0.

Функция Найти() не поддерживает направление поиска, начальную позицию и номер вхождения — она всегда ищет первое вхождение с начала строки. Если в старом коде требуется поиск с конца, придётся либо переходить на СтрНайти(), либо реализовывать цикл вручную.

// Старый вариант

Позиция = Найти("Привет, мир", "мир"); // 9

// Эквивалент на современном синтаксисе

Позиция = СтрНайти("Привет, мир", "мир"); // 9

СтрНайти или Найти

СтрНайти

Рекомендуемый вариант: поддерживает направление поиска, начальную позицию и номер вхождения. Используйте во всём новом коде

Найти

Устаревшая функция, оставлена для совместимости. Только первое вхождение с начала строки, обратный порядок параметров. Не применяйте в новых разработках

СтрЧислоВхождений

Когда нужно только количество вхождений без позиций — например, проверить, что разделитель встречается ровно один раз

Извлечение найденной части строки

Найти позицию — половина задачи: обычно дальше нужно вырезать фрагмент. Для этого служат Лев(), Прав() и Сред(). Типичный сценарий — взять всё до разделителя или после него.

Строка = "Артикул: АБВ-12345";

Позиция = СтрНайти(Строка, ":");

Если Позиция > 0 Тогда

Значение = СокрЛП(Сред(Строка, Позиция + 1));

// Значение = "АБВ-12345"

КонецЕсли;

Обязательно проверяйте, что вхождение найдено, прежде чем вызывать Сред(). Если позиция равна 0, вы получите либо всю строку, либо некорректный фрагмент — в зависимости от арифметики смещения. Такая ошибка не вызывает исключения и потому долго остаётся незамеченной.

Если строка содержит несколько однотипных частей, разделённых одинаковым символом (CSV-строки, составные номера), удобнее не искать вхождения по одному, а разложить строку функцией СтрРазделить() в массив и работать с элементами по индексам.

Порядок действий при разборе строки
  1. 1
    Определите разделитель или искомый фрагмент и проверьте, может ли его не быть в строке
  2. 2
    Выполните поиск: Позиция = СтрНайти(Строка, Разделитель)
  3. 3
    Проверьте результат: Если Позиция > 0 Тогда — иначе предусмотрите ветку для строки без разделителя
  4. 4
    Извлеките нужную часть через Лев(), Сред() или Прав() и обрежьте пробелы функцией СокрЛП()
  5. 5
    Проверьте результат на тестовых строках: с фрагментом в начале, в середине, в конце и без фрагмента

Альтернативы: СтрШаблон, регулярные выражения и поиск в запросах

Когда искомый шаблон сложнее простой подстроки (например, «цифры между дефисами»), встроенных строковых функций недостаточно. В толстом клиенте и на сервере Windows доступен COM-объект VBScript.RegExp для регулярных выражений, но он не работает в веб-клиенте и на Linux-сервере — учитывайте это при проектировании.

Если поиск части строки нужен для отбора данных, не выгружайте записи и не перебирайте их в цикле — используйте оператор ПОДОБНО в запросе: ГДЕ Наименование ПОДОБНО "%кабель%". Это выполняется на стороне СУБД и на больших таблицах работает на порядки быстрее программного перебора.

В динамических списках и формах выбора поиск по подстроке уже встроен в платформу: пользователь вводит текст, и список фильтруется автоматически. Программно это поведение настраивается через свойства поля ввода «Способ поиска строки» и «Поиск при вводе», без написания собственных функций поиска.

Проверка результата и отладка

Надёжный способ убедиться, что поиск работает, — проверить его на четырёх граничных случаях: подстрока в начале строки, в середине, в конце и полное отсутствие подстроки. Также проверьте строку, где искомый фрагмент встречается несколько раз, если используете номер вхождения или поиск с конца.

Для отладки используйте конфигуратор: точка останова перед строкой поиска и просмотр значений Строка и Позиция в табло. Часто оказывается, что в строке есть неразрывные пробелы или невидимые символы из скопированного текста — тогда визуально совпадающая подстрока не находится. Очистить строку от лишних пробелов помогает СокрЛП(), а от неразрывных пробелов — СтрЗаменить(Строка, Символ(160), " ").

Контрольные точки

Как проверить результат

Возвращаемая позиция
Норма

Число от 1 до длины строки при найденной подстроке, 0 при отсутствии

Тревожный признак

Позиция 0 при визуально присутствующем фрагменте — вероятны различия в регистре или невидимые символы

Извлечённый фрагмент
Норма

Фрагмент совпадает с ожидаемым, без лишних пробелов по краям

Тревожный признак

Лишний первый символ или обрезанный конец — ошибка в арифметике смещения Сред()

Поведение на строке без вхождения
Норма

Выполняется отдельная ветка кода, исключений нет

Тревожный признак

Сред() вызывается при позиции 0 и возвращает некорректные данные молча

При массовой обработке данных (например, разборе тысяч строк из файла) измеряйте время выполнения: вложенные циклы поиска по длинным строкам могут стать узким местом. В таких случаях выносите инвариантные вычисления из цикла и по возможности заменяйте программный перебор запросом.

Итог

Что важно запомнить

Используйте СтрНайти(Строка, Подстрока) — функция возвращает позицию вхождения или 0. Проверяйте результат явным сравнением с нулём перед извлечением фрагмента. Для поиска без учёта регистра приводите обе строки через НРег(). Устаревшую Найти() не применяйте в новом коде, а при её замене помните про обратный порядок параметров. Для отбора данных по части строки используйте ПОДОБНО в запросе вместо программного перебора.

Частые вопросы

Что возвращает СтрНайти, если подстрока не найдена?

Функция возвращает число 0. Поэтому проверка наличия вхождения записывается как «Если СтрНайти(Строка, Подстрока) > 0 Тогда». Полагаться на неявное приведение числа к булеву не стоит — явное сравнение читается однозначно и защищает от ошибок при доработке кода.

Как найти часть строки без учёта регистра?

Приведите обе строки к одному регистру перед поиском: СтрНайти(НРег(Строка), НРег(Подстрока)). Подойдёт и ВРег() — важно лишь применить одну и ту же функцию к обоим аргументам. Преобразование не затрагивает цифры и спецсимволы, поэтому способ безопасен для смешанных строк.

Чем отличаются СтрНайти и Найти?

Главное отличие — порядок параметров: Найти(Подстрока, Строка) против СтрНайти(Строка, Подстрока). Кроме того, СтрНайти поддерживает направление поиска, начальную позицию и номер вхождения, а Найти всегда ищет первое вхождение с начала. Найти считается устаревшей и сохраняется только для совместимости со старым кодом.

Как найти последнее вхождение подстроки?

Передайте третьим параметром НаправлениеПоиска.СКонца: СтрНайти(Строка, Подстрока, НаправлениеПоиска.СКонца). Функция вернёт позицию последнего вхождения. Это стандартный приём для отделения имени файла от пути или расширения от имени.

Как проверить, что строка начинается с определённого фрагмента?

Сравните результат СтрНайти с единицей: Если СтрНайти(Строка, "Префикс") = 1 Тогда — строка начинается с указанного фрагмента. Альтернатива — сравнение Лев(Строка, СтрДлина("Префикс")) с самим фрагментом, что иногда читается нагляднее.

Как найти подстроку в таблице значений или справочнике?

Перебор строк с СтрНайти подходит только для небольших объёмов. Для отбора по базе данных используйте запрос с оператором ПОДОБНО и шаблоном "%фрагмент%" — он выполняется на стороне СУБД. Для уже загруженной таблицы значений можно использовать метод НайтиСтроки с предварительно вычисленной колонкой или отбор через построитель, но простой цикл со СтрНайти тоже допустим при умеренном числе строк.