Как в 1С корректно определить регион по адресу, избежав ошибок поиска?

Программист IT и автоматизация бизнеса
← На главную

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

В этой статье разберем, как обойти эту ловушку. Проанализируем несколько методов — от простых строковых "хитростей" до более системных и надежных подходов с использованием внешних сервисов.

Проблема: ложное срабатывание при поиске подстроки

Давайте проанализируем ситуацию автора. Есть адресная строка, например, "обл Томская, г Стрежевой, ул Ке...". Задача — определить код региона. Разработчик пишет код, который последовательно проверяет наличие названий регионов в строке:


// ... предыдущие проверки
Если СтрНайти(АдреснаяСтрока, "Омская") > 0 Тогда
    Возврат 6; // Код для Омской области
КонецЕсли;

Если СтрНайти(АдреснаяСтрока, "Томская") > 0 Тогда
    Возврат 7; // Код для Томской области
КонецЕсли;
// ... последующие проверки

Когда в этот код попадает строка "обл Томская", первая же проверка на "Омская" дает положительный результат, так как "Омская" является частью слова "Томская". В итоге система неверно определяет регион. Рассмотрим, как это можно исправить.

Решение 1: Изменение порядка проверок

Самое быстрое, но не самое надежное решение — изменить порядок проверок. Если мы знаем о таких "конфликтующих" парах, можно сначала искать более длинную или специфичную строку, а затем — более короткую.


// Сначала ищем "Томская"
Если СтрНайти(АдреснаяСтрока, "Томская") > 0 Тогда
    Возврат 7; // Код для Томской области
// И только потом "Омская"
ИначеЕсли СтрНайти(АдреснаяСтрока, "Омская") > 0 Тогда
    Возврат 6; // Код для Омской области
КонецЕсли;

Преимущества: Простота реализации.

Недостатки: Этот метод плохо масштабируется. Вам придется вручную отслеживать все подобные пары ("томская"/"томбовская", "пензенская"/"пермская" и т.д.) и постоянно поддерживать правильный порядок условий, что трудоемко и чревато ошибками.

Решение 2: Поиск по целому слову с использованием пробелов

Более надежный подход — убедиться, что мы ищем целое слово, а не его часть. Самый простой способ сделать это — включить в поисковую строку пробелы-ограничители. Это позволяет отсечь вхождения внутри других слов.

Рассмотрим два варианта этого метода.

1. Добавление пробела в начало поисковой строки.

Мы ищем не "Томская", а " Томская" (с пробелом вначале). Это гарантирует, что перед найденным словом будет разделитель.


// Ищем с пробелом в начале
Если СтрНайти(АдреснаяСтрока, " Томская") > 0 Тогда
    Возврат 7;
КонецЕсли;

Однако этот код не сработает, если название региона стоит в самом начале строки, например, "Томская обл., г. Томск...". Чтобы решить эту проблему, можно добавить пробел в начало анализируемой строки перед поиском.


ПроверяемыйАдрес = " " + АдреснаяСтрока; // Добавляем пробел в начало

// Теперь поиск " Томская" сработает для любого положения региона в строке
Если СтрНайти(ПроверяемыйАдрес, " Томская") > 0 Тогда
    Возврат 7; 
ИначеЕсли СтрНайти(ПроверяемыйАдрес, " Омская") > 0 Тогда
    Возврат 6;
КонецЕсли;

2. Проверка символа перед найденной позицией.

Это более универсальный и "академически" правильный способ. Сначала мы находим позицию вхождения подстроки, а затем проверяем, какой символ находится перед ней.

  1. Находим позицию слова, например, "Омская", с помощью СтрНайти().
  2. Если позиция больше 1, проверяем символ на (Позиция - 1). Это должен быть небуквенный символ, чаще всего пробел.
  3. Если позиция равна 1, это означает, что слово стоит в начале строки, и это тоже корректный случай.

Посмотрим на пример кода, реализующего эту логику:


Функция НайтиЦелоеСлово(Источник, ИскомоеСлово)

    Позиция = СтрНайти(Источник, ИскомоеСлово);

    Пока Позиция > 0 Цикл
        // Проверяем, что это начало строки
        Если Позиция = 1 Тогда
            Возврат Истина; // Слово в самом начале
        КонецЕсли;

        // Проверяем символ перед найденным словом
        СимволПеред = Сред(Источник, Позиция - 1, 1);
        Если СимволПеред = " " ИЛИ СимволПеред = "," // Можно добавить и другие разделители
           ИЛИ СимволПеред = "." Тогда
           Возврат Истина; // Найден разделитель, значит, это целое слово
        КонецЕсли;
        
        // Если проверка не прошла, ищем следующее вхождение
        Позиция = СтрНайти(Источник, ИскомоеСлово, , Позиция + 1);
    КонецЦикла;

    Возврат Ложь;

КонецФункции

// Пример использования
Если НайтиЦелоеСлово(АдреснаяСтрока, "Омская") Тогда
    Возврат 6;
ИначеЕсли НайтиЦелоеСлово(АдреснаяСтрока, "Томская") Тогда
    Возврат 7;
КонецЕсли;

Этот метод самый гибкий из всех "ручных" строковых проверок, так как позволяет точно идентифицировать слова.

Решение 3: Разбор фрагмента адреса на слова

Еще один надежный способ — разбить часть строки, содержащую регион, на отдельные слова, а затем искать точное совпадение в получившемся массиве.

  1. Извлекаем первую часть адреса до запятой: "обл Томская".
  2. Используем функцию СтрРазделить(), чтобы получить массив слов: ["обл", "Томская"].
  3. Проверяем наличие точного совпадения в массиве.

// Получаем первую часть адреса
ФрагментАдреса = "";
ПозицияЗапятой = СтрНайти(АдреснаяСтрока, ",");
Если ПозицияЗапятой > 0 Тогда
    ФрагментАдреса = Лев(АдреснаяСтрока, ПозицияЗапятой - 1);
Иначе
    ФрагментАдреса = АдреснаяСтрока;
КонецЕсли;

// Разделяем на слова, убирая лишние пробелы
МассивСлов = СтрРазделить(СокрЛП(ФрагментАдреса), " ", Ложь);

// Ищем точное совпадение в массиве
Для Каждого Слово Из МассивСлов Цикл
    Если НРег(Слово) = "томская" Тогда
        Возврат 7; // Найдена Томская область
    ИначеЕсли НРег(Слово) = "омская" Тогда
        Возврат 6; // Найдена Омская область
    КонецЕсли;
КонецЦикла;

Примечание: Использование НРег() (приведение к нижнему регистру) здесь оправдано, так как адрес может быть введен в разном регистре ("Томская", "томская", "ТОМСКАЯ"). Сравнивая отдельные слова, мы уже не рискуем найти подстроку.

Решение 4: Современные и профессиональные подходы

Хотя разобранные выше методы решают поставленную задачу, для промышленных систем, где важна высокая точность и надежность, рекомендуется использовать более системные инструменты.

1. Использование Библиотеки стандартных подсистем (БСП) 1С.

Если ваша конфигурация использует БСП и адреса вводятся структурированно через адресный классификатор (ФИАС/ГАР), то нет нужды "парсить" строки вручную. В общем модуле РаботаСАдресами есть готовые функции, например, РегионАдресаКонтактнойИнформации, которые вернут вам субъект РФ из уже сохраненного структурированного адреса.

2. Внешние API для стандартизации адресов (DaData, Яндекс.Карты).

Это самый мощный способ обработки неструктурированных, произвольно введенных адресов. Вы отправляете строку с адресом на сервер специального сервиса, а в ответ получаете полностью разобранный и стандартизированный адрес, включая регион, город, улицу, почтовый индекс, координаты и часовой пояс.

Интеграция с такими сервисами через HTTP-запросы — это современный стандарт для решения подобных задач, который избавляет от необходимости писать и поддерживать сложные алгоритмы разбора строк.

Таким образом, для быстрой локальной задачи подойдут методы поиска с пробелами или разбиение на слова. Но для надежной и масштабируемой системы настоятельно рекомендуется смотреть в сторону API-сервисов или встроенных возможностей БСП.

← На главную