Сравнение строк во встроенном языке и запросах 1С 8.3

· 9 мин

Содержание

Чтобы сравнить две строки в 1С, проще всего написать Стр1 = Стр2. Оператор сравнивает строки посимвольно и учитывает регистр: "Счет" = "счет" даст Ложь. Если регистр не важен, используйте СтрСравнить(Стр1, Стр2) = 0 (платформа 8.3.6 и новее) или ВРег(Стр1) = ВРег(Стр2).

Сложности начинаются с пробелов, невидимых символов, буквы «ё» и сравнения в запросах. Тут результат зависит уже не от языка, а от данных и СУБД. Все примеры кода подходят для внешней обработки или общего модуля и работают в платформе 8.3 с интерфейсом «Такси».

Сравнение строк оператором = и <>

Оператор = возвращает Истина, только если строки совпадают полностью: та же длина, те же символы в том же регистре. <> — обратный результат.

Стр1 = "ООО Ромашка";
Стр2 = "ООО ромашка";

Если Стр1 = Стр2 Тогда
    Сообщить("Совпадают");
Иначе
    Сообщить("Различаются"); // сработает эта ветка: разный регистр
КонецЕсли;

Что ещё важно знать про =:

  • 🔤 Регистр учитывается. «Москва» и «МОСКВА» — разные строки.
  • ␣ Пробелы значимы. "Ромашка " с пробелом в конце не равна "Ромашка".
  • 🔢 Типы не приводятся. "123" = 123 вернёт Ложь без ошибки. Сначала приведите значения к одному типу: Строка(Число) или Число(Строка).
  • ∅ Неопределено не равно пустой строке. Неопределено = "" даёт Ложь. Для проверки «пусто или одни пробелы» есть ПустаяСтрока(Стр).

Операторы < и > для строк тоже допустимы, но для сортировки по алфавиту и проверки «какая строка раньше» удобнее функция СтрСравнить: она не зависит от регистра и возвращает понятный результат.

Сравнивая число со строкой, приводите число через Формат(Число, "ЧГ=0"). Обычное Строка(1500) даст "1 500" с разделителем групп, и сравнение со строкой "1500" провалится.

СтрСравнить: сравнение строк без учёта регистра

Функция СтрСравнить(Строка1, Строка2) появилась в платформе 8.3.6. Она сравнивает строки без учёта регистра и возвращает число:

РезультатЧто значит
0строки равны (с точностью до регистра)
-1первая строка меньше второй
1первая строка больше второй

Пример проверки на равенство:

Если СтрСравнить(Контрагент.Наименование, ИскомоеИмя) = 0 Тогда
    Сообщить("Найден контрагент");
КонецЕсли;

Частая ошибка — писать Если СтрСравнить(А, Б) Тогда. Функция возвращает число, а не булево значение. Условие с числом вызовет ошибку преобразования типа. Всегда сравнивайте результат с нулём явно.

Результат -1/1 пригодится, когда сортируете строки вручную, например пузырьком или вставкой в упорядоченный массив.

Если платформа старше 8.3.6

В старых базах СтрСравнить нет. Приведите обе строки к одному регистру:

Если ВРег(Стр1) = ВРег(Стр2) Тогда
    // равны без учёта регистра
КонецЕсли;

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

Какой способ выбрать

СпособРегистрКогда использовать
Стр1 = Стр2учитываеткоды, артикулы, пароли, GUID, точное совпадение
СтрСравнить(Стр1, Стр2) = 0не учитываетимена, наименования, ввод пользователя
ВРег(Стр1) = ВРег(Стр2)не учитываетплатформа ниже 8.3.6 или нормализованный ключ для поиска
СтрНайти(Стр1, Стр2) > 0учитываетпроверка вхождения подстроки
СтрНачинаетсяС / СтрЗаканчиваетсяНаучитываетпроверка начала или конца строки (8.3.6+)

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

Классика: две строки на экране выглядят одинаково, а = возвращает Ложь. Почти всегда виноваты символы, которых не видно.

Кто чаще всего мешает сравнению:

  • ␣ Пробелы по краям. Их убирает СокрЛП(Стр). Для одной стороны — СокрЛ и СокрП.
  • 📏 Строки фиксированной длины. Реквизит с фиксированной длиной дополняется пробелами справа. Значение "Ромашка" в реквизите длиной 20 придёт с хвостом пробелов. Перед сравнением делайте СокрП.
  • ⎵ Неразрывный пробел. Приходит из Word, Excel и с сайтов. В коде это Символы.НПП. На вид обычный пробел, но код символа другой — 160 вместо 32.
  • ↵ Переводы строк. Текст из файла Windows содержит пару Символы.ВК + Символы.ПС, из других систем — только Символы.ПС.
  • 👻 Символ нулевой ширины (код 8203) и метка порядка байтов (код 65279) в начале строки из файла. Их не видно вообще.

Как найти символ, из-за которого строки различаются

Ищите первую позицию расхождения и выводите коды символов в этом месте:

Функция ПозицияРасхождения(Стр1, Стр2)
    Предел = Мин(СтрДлина(Стр1), СтрДлина(Стр2));
    Для Поз = 1 По Предел Цикл
        Если Сред(Стр1, Поз, 1) <> Сред(Стр2, Поз, 1) Тогда
            Возврат Поз;
        КонецЕсли;
    КонецЦикла;
    Если СтрДлина(Стр1) <> СтрДлина(Стр2) Тогда
        Возврат Предел + 1;
    КонецЕсли;
    Возврат 0; // строки равны
КонецФункции

Поз = ПозицияРасхождения(Стр1, Стр2);
Если Поз > 0 Тогда
    Сообщить(СтрШаблон("Позиция %1: код в первой строке %2, во второй %3",
        Формат(Поз, "ЧГ=0"),
        Формат(КодСимвола(Стр1, Поз), "ЧГ=0"),
        Формат(КодСимвола(Стр2, Поз), "ЧГ=0")));
КонецЕсли;

Если функция вернула позицию на единицу больше длины короткой строки, начало у строк общее, а разница в «хвосте». Чаще всего там пробелы или перевод строки. Код выведите только для длинной строки: у короткой в этой позиции символа нет.

По коду символа понятно, что делать дальше:

КодСимволЧто делать
32обычный пробелСокрЛП, схлопнуть двойные пробелы
160неразрывный пробелСтрЗаменить(Стр, Символы.НПП, " ")
9табуляциязаменить на пробел
10, 13перевод строки, возврат кареткиудалить или заменить на пробел
8203пробел нулевой шириныудалить
65279метка порядка байтовудалить
Диагностика: выберите симптом

Сравнение строк в запросах 1С

В языке запросов строки тоже сравнивают через = и <>. Но поведение здесь задаёт СУБД, а не встроенный язык.

Регистр и пробелы в запросе

С типичными настройками баз 1С (файловая база, MS SQL с параметрами по умолчанию, PostgreSQL в сборке для 1С) сравнение строк в запросе обычно идёт без учёта регистра. То есть условие ГДЕ Наименование = &Имя найдёт и «Ромашка», и «РОМАШКА». Во встроенном языке такое же сравнение регистр учтёт.

Отсюда частый сюрприз: запрос нашёл запись, а проверка в коде Выборка.Наименование = Имя её отбросила. Если результат нужен одинаковый, сравнивайте в коде через СтрСравнить.

Не стройте логику на том, что запрос «не видит» регистр. Это свойство конкретной СУБД и её настроек, а не гарантия платформы. Для однозначного результата на платформе 8.3.20 и новее приводите обе стороны явно: ГДЕ ВРЕГ(Наименование) = ВРЕГ(&Имя). Учтите, что функция над полем мешает использовать индекс.

Поля неограниченной длины

Строковый реквизит неограниченной длины нельзя напрямую сравнивать в условии запроса — платформа выдаст ошибку. Обрежьте поле через ВЫРАЗИТЬ:

ВЫБРАТЬ
    Док.Ссылка
ИЗ
    Документ.ПоступлениеТоваровУслуг КАК Док
ГДЕ
    ВЫРАЗИТЬ(Док.Комментарий КАК СТРОКА(200)) = &Комментарий

Длину в СТРОКА(200) берите с запасом под реальные данные. Текст длиннее будет обрезан, и две длинные строки с одинаковым началом окажутся «равны».

Поиск по шаблону: ПОДОБНО

Для частичного совпадения есть ПОДОБНО. Символ % — любая последовательность, _ — ровно один любой символ:

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Наименование ПОДОБНО &Шаблон
Запрос.УстановитьПараметр("Шаблон", "%" + ИскомыйТекст + "%");

Если в искомом тексте сами есть % или _ (например, «скидка 10%»), экранируйте их и укажите спецсимвол:

ГДЕ Номенклатура.Наименование ПОДОБНО &Шаблон СПЕЦСИМВОЛ "~"
Текст = СтрЗаменить(ИскомыйТекст, "~", "~~");
Текст = СтрЗаменить(Текст, "%", "~%");
Текст = СтрЗаменить(Текст, "_", "~_");
Запрос.УстановитьПараметр("Шаблон", "%" + Текст + "%");

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

ПОДОБНО "%текст%" с процентом в начале не использует индекс. На таблице в сотни тысяч строк такой запрос заметно медленнее точного сравнения.

Национальные символы: «ё», латиница и составные буквы

Русский текст добавляет свои ловушки. ВРег и СтрСравнить их не лечат.

  • 🇷🇺 «Ё» и «е». Это разные символы. «Алёна» и «Алена» не равны ни через =, ни через СтрСравнить. Если для задачи это одно и то же, заменяйте «ё» на «е» в обеих строках.
  • 🔠 Латиница, похожая на кириллицу. Буквы А, В, Е, К, М, Н, О, Р, С, Т, Х есть в обоих алфавитах. «OOO Ромашка» с латинскими «O» выглядит нормально, но не совпадёт. Проверка — тот же КодСимвола: у кириллической «О» код 1054, у латинской «O» — 79.
  • 🧩 Составные буквы. Буква «й» в тексте из некоторых программ и PDF записана двумя символами: «и» плюс отдельный знак краткости (код 774). На экране одна буква, длина строки на единицу больше.

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

Нормализация и быстрое сравнение больших списков

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

Функция НормализоватьСтроку(Знач Стр)
    Стр = СтрЗаменить(Стр, Символы.НПП, " ");
    Стр = СтрЗаменить(Стр, Символы.Таб, " ");
    Стр = СтрЗаменить(Стр, Символы.ВК, "");
    Стр = СтрЗаменить(Стр, Символы.ПС, " ");
    Стр = СтрЗаменить(Стр, Символ(8203), "");
    Стр = СтрЗаменить(Стр, Символ(65279), "");
    Стр = СтрЗаменить(Стр, "ё", "е");
    Стр = СтрЗаменить(Стр, "Ё", "Е");
    Пока СтрНайти(Стр, "  ") > 0 Цикл
        Стр = СтрЗаменить(Стр, "  ", " ");
    КонецЦикла;
    Возврат НРег(СокрЛП(Стр));
КонецФункции

Знач в параметре нужен, чтобы функция не изменила переменную вызывающего кода.

Не сравнивайте в двойном цикле

Вложенный цикл «каждая строка файла с каждым элементом справочника» на 5 000 × 5 000 строк — это 25 миллионов сравнений. Быстрее один раз сложить нормализованные значения в Соответствие и искать по ключу:

Индекс = Новый Соответствие;
Для Каждого Строка Из ТаблицаСправочника Цикл
    Индекс.Вставить(НормализоватьСтроку(Строка.Наименование), Строка.Ссылка);
КонецЦикла;

Для Каждого СтрокаФайла Из ТаблицаФайла Цикл
    Ссылка = Индекс.Получить(НормализоватьСтроку(СтрокаФайла.Наименование));
    Если Ссылка = Неопределено Тогда
        // не найдено в справочнике
    КонецЕсли;
КонецЦикла;

Ключи Соответствия сравниваются с учётом регистра, поэтому нормализация с НРег здесь обязательна.

Короткие правила

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

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

Можно ли сравнить строки с учётом регистра через СтрСравнить?
Нет, функция всегда игнорирует регистр. Для точного сравнения используйте оператор =.
Почему сравнение через = в запросе и в коде даёт разный результат?
В коде регистр учитывается всегда. В запросе правила задаёт СУБД, и в типовых настройках регистр не учитывается.
Как проверить, что строка начинается с нужного текста?
СтрНачинаетсяС(Стр, "ООО") в платформе 8.3.6 и новее. В старых версиях — Лев(Стр, 3) = "ООО".
Как сравнить строку с пустой?
ПустаяСтрока(Стр) вернёт Истина и для пустой строки, и для строки только из пробелов. Сравнение Стр = "" пробелы не простит.
Как сравнить две многострочные строки построчно?
Разбейте каждую через СтрРазделить(Стр, Символы.ПС), удалите Символы.ВК, затем сравнивайте элементы массивов по индексу.