Сравнение строк во встроенном языке и запросах 1С 8.3
Содержание
Чтобы сравнить две строки в 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 миллионов сравнений. Быстрее один раз сложить нормализованные значения в Соответствие и искать по ключу:
Индекс = Новый Соответствие;
Для Каждого Строка Из ТаблицаСправочника Цикл
Индекс.Вставить(НормализоватьСтроку(Строка.Наименование), Строка.Ссылка);
КонецЦикла;
Для Каждого СтрокаФайла Из ТаблицаФайла Цикл
Ссылка = Индекс.Получить(НормализоватьСтроку(СтрокаФайла.Наименование));
Если Ссылка = Неопределено Тогда
// не найдено в справочнике
КонецЕсли;
КонецЦикла;
Ключи Соответствия сравниваются с учётом регистра, поэтому нормализация с НРег здесь обязательна.
Короткие правила
- Коды, артикулы, ИНН, GUID сравнивайте точно через
=, послеСокрЛП. - Имена и ввод пользователя — через
СтрСравнить(...) = 0или нормализацию. - Данные из файлов и буфера обмена всегда прогоняйте через нормализацию.
- В запросе приводите регистр явно, если результат должен совпасть с проверкой в коде.
- Нормализованное значение для частых сверок храните в отдельном реквизите, а не считайте каждый раз.
Частые вопросы
Можно ли сравнить строки с учётом регистра через СтрСравнить?
=.Почему сравнение через = в запросе и в коде даёт разный результат?
Как проверить, что строка начинается с нужного текста?
СтрНачинаетсяС(Стр, "ООО") в платформе 8.3.6 и новее. В старых версиях — Лев(Стр, 3) = "ООО".Как сравнить строку с пустой?
ПустаяСтрока(Стр) вернёт Истина и для пустой строки, и для строки только из пробелов. Сравнение Стр = "" пробелы не простит.Как сравнить две многострочные строки построчно?
СтрРазделить(Стр, Символы.ПС), удалите Символы.ВК, затем сравнивайте элементы массивов по индексу.