Как реализовать перебор визуально похожих символов (кириллица, латиница, цифры) в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача поиска данных, которые могут быть введены в разных раскладках. Это актуально для артикулов, VIN-номеров автомобилей, серийных номеров или государственных знаков. Пользователи часто путают визуально идентичные символы, такие как русская "А" и английская "A", или цифру "0" и букву "О". Разберем, как правильно реализовать алгоритм перебора всех возможных комбинаций написания слова и какие нюансы следует учитывать для обеспечения стабильности системы.

Анализ проблемы омографов

Символы, которые выглядят одинаково, но имеют разные коды в таблице Unicode, называются омографами. Для платформы 1С символы "А" (кириллица) и "A" (латиница) — это абсолютно разные объекты. Если в базе данных артикул записан латиницей, а пользователь ищет его, используя русскую раскладку, стандартный поиск не даст результатов. Чтобы решить эту задачу, программисты обычно используют два подхода: либо нормализацию (приведение всех строк к одному стандарту при записи), либо генерацию вариантов (поиск по всем возможным комбинациям написания).

Разбор рекурсивного алгоритма перебора

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


&НаКлиенте
Процедура Команда1(Команда)
    СписокПохожестей = Новый Массив;
    СписокПохожестей.Добавить("АаAa");
    СписокПохожестей.Добавить("РрPp");
    СписокПохожестей.Добавить("ОоOo0");
    
    ВходноеСлово = "АРО";
    
    ПеребратьСимволВСлове(СписокПохожестей, ВходноеСлово, СтрДлина(ВходноеСлово));
КонецПроцедуры

Процедура ПеребратьСимволВСлове(СписокПохожестей, Знач ВходноеСлово, Знач НомерСимвола)
    Если НомерСимвола < 1 Тогда 
        Сообщить(ВходноеСлово);
        Возврат; 
    КонецЕсли;
    
    ПроверяемыйСимвол = Сред(ВходноеСлово, НомерСимвола, 1);
    Для Каждого СтрокаПохожести из СписокПохожестей Цикл
        Если СтрНайти(СтрокаПохожести, ПроверяемыйСимвол) > 0 Тогда
            ЛеваяЧастьСлова  = Лев(ВходноеСлово, НомерСимвола - 1);
            ПраваяЧастьСлова = Сред(ВходноеСлово, НомерСимвола + 1);
            Для НомерВСтрокеПохожести = 1 по СтрДлина(СтрокаПохожести) Цикл
                НовоеСлово = ЛеваяЧастьСлова + Сред(СтрокаПохожести, НомерВСтрокеПохожести, 1) + ПраваяЧастьСлова;
                ПеребратьСимволВСлове(СписокПохожестей, НовоеСлово, НомерСимвола - 1);
            КонецЦикла;
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

В данном коде используется классическая рекурсия. Однако при работе с длинными строками или большим количеством похожих символов возникает проблема экспоненциального роста количества комбинаций. Например, если в слове 10 заменяемых позиций и у каждой по 2 варианта, мы получим 1024 итерации. Если же вариантов замены больше (как в случае с "О", "о", "O", "o", "0"), количество комбинаций быстро переполнит стек вызовов, что приведет к аварийному завершению работы 1С.

Оптимизация через нормализацию символов

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


Функция ЗаменитьЛатыньНаКирилицу(ТекстоваяСтрока)
    Лат = "AaBCcEeHKkMmnOoPpTuXxy";
    Рус = "АаВСсЕеНКкМтпОоРрТиХху";
    Результат = ТекстоваяСтрока;
    Для сч = 1 по СтрДлина(Результат) Цикл
        тПоз = Найти(Лат, Сред(Результат, сч, 1));
        Если тПоз > 0 Тогда
            // Заменяем конкретный символ по его позиции в эталонной строке
            Результат = СтрЗаменить(Результат, Сред(Лат, тПоз, 1), Сред(Рус, тПоз, 1));
        КонецЕсли;    
    КонецЦикла;    
    Возврат Результат;
КонецФункции

Функция ЗаменитьКирилицуНаЛатынь(ТекстоваяСтрока)
    Лат = "AaBCcEeHKkMmnOoPpTuXxy";
    Рус = "АаВСсЕеНКкМтпОоРрТиХху";
    Результат = ТекстоваяСтрока;
    Для сч = 1 по СтрДлина(Результат) Цикл
        тПоз = Найти(Рус, Сред(Результат, сч, 1));
        Если тПоз > 0 Тогда
            Результат = СтрЗаменить(Результат, Сред(Рус, тПоз, 1), Сред(Лат, тПоз, 1));
        КонецЕсли;    
    КонецЦикла;    
    Возврат Результат;
КонецФункции

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

Использование пакетных запросов (Декартово произведение)

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

Суть метода заключается в следующем:

  1. Для каждой позиции символа во входящем слове создается временная таблица.
  2. В эту таблицу помещаются все возможные варианты данного символа.
  3. В итоговом запросе эти таблицы соединяются между собой без условий соединения (Cross Join).

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

Расширенный список символов-двойников

Для создания действительно качественной системы поиска, проанализируем наиболее полный список символов, которые визуально неразличимы в большинстве шрифтов:

Важно помнить, что функция СтрНайти и оператор ПОДОБНО в запросах могут вести себя по-разному в зависимости от настроек чувствительности к регистру в базе данных (Collation SQL), но они всегда чувствительны к раскладке (Unicode-коду символа).

Рекомендации по реализации

Подводя итог, проанализируем лучшие практики для решения задачи перебора:

  1. Избегайте глубокой рекурсии. Если вам все же нужно генерировать варианты в коде, используйте итеративные циклы с ограничением максимального количества комбинаций.
  2. Используйте индексацию. Всегда лучше хранить в базе данных подготовленную (нормализованную) строку для поиска.
  3. Регулярные выражения. Если задача стоит в поиске подстроки в тексте, рассмотрите использование RegExp (через COM-объект VBScript.RegExp), где для каждой позиции можно задать набор альтернатив, например [АA][РP][ОO0].
  4. Проверка на клиенте. Если перебор выполняется для вывода вариантов пользователю, делайте это на стороне сервера, чтобы не перегружать клиентское приложение и не вызывать зависание интерфейса.

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

← На главную