В практике разработчика 1С часто возникает ситуация, когда код, основанный на сравнении строковых данных, начинает вести себя непредсказуемо. Одной из самых распространенных причин путаницы является различие в логике работы встроенного языка и языка запросов при сопоставлении строк с разным регистром символов. Рассмотрим детально, почему выражение "Вася" = "вася" может возвращать разные результаты в зависимости от контекста выполнения, и разберем лучшие практики решения этой задачи.
Первое и самое важное правило, которое нам необходимо усвоить: встроенный язык 1С всегда чувствителен к регистру при использовании операторов сравнения (=, <>). Если мы выполняем проверку непосредственно в программном коде модуля, то символы "В" и "в" будут считаться абсолютно разными объектами из-за различия их кодов в таблице символов.
Проанализируем пример кода, который часто вводит в заблуждение начинающих разработчиков:
Если "Вася" = "вася" Тогда
// Этот блок никогда не выполнится
Сообщить("Строки равны");
Иначе
// Программа всегда зайдет сюда
Сообщить("Строки не равны");
КонецЕсли;
В то же время, если мы напишем запрос к базе данных, поведение системы кардинально изменится. Язык запросов 1С по умолчанию нечувствителен к регистру. Это связано с тем, что физические СУБД (MS SQL Server, PostgreSQL), на которых работает 1С, настраиваются платформой с использованием определенных параметров сопоставления (Collation).
Посмотрим на пример запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ""Равны"" КАК Результат
|ГДЕ
| ""Вася"" = ""вася""";
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
// Запрос вернет строку "Равны"
КонецЕсли;
Такое расхождение часто приводит к ошибкам: разработчик проверяет условие в консоли запросов, видит, что данные находятся, а затем пишет аналогичное условие в коде на встроенном языке, и оно перестает работать.
Чтобы выполнить регистронезависимое сравнение во встроенном языке, мы должны явно привести обе строки к одному регистру — либо к верхнему, либо к нижнему. Для этого используются функции ВРег() (Верхний Регистр) и НРег() (Нижний Регистр).
Разберем, как правильно модифицировать проверку:
// Приведение к верхнему регистру
Если ВРег("Вася") = ВРег("вася") Тогда
Сообщить("Теперь строки равны, так как обе стали 'ВАСЯ'");
КонецЕсли;
// Приведение к нижнему регистру
Если НРег("Вася") = НРег("вася") Тогда
Сообщить("Теперь строки равны, так как обе стали 'вася'");
КонецЕсли;
Выбор между ВРег() и НРег() обычно остается на усмотрение разработчика. Некоторые предпочитают НРег(), так как визуально строчные буквы воспринимаются легче, другие выбирают ВРег(), следуя традиции типовых конфигураций, где поиск часто реализован через верхний регистр.
Начиная с версии платформы 8.3.10, в распоряжении программистов появился более изящный и мощный инструмент — функция СтрСравнить(). Она позволяет сравнивать строки без создания промежуточных копий строк в памяти, которые неизбежны при использовании ВРег().
Рассмотрим синтаксис этой функции:
СтрСравнить(Строка1, Строка2, ВариантСравненияСтрок.ИгнорироватьРегистр)
Преимущества использования СтрСравнить:
Важно помнить о нюансах работы СУБД. Хотя в запросах 1С сравнение ГДЕ Поле = "значение" не учитывает регистр, попытка принудительно использовать функцию ВРЕГ() внутри запроса может фатально замедлить работу системы.
Проанализируем плохой пример кода в запросе:
// ОПАСНО ДЛЯ ПРОИЗВОДИТЕЛЬНОСТИ
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ ВРЕГ(Наименование) = &П_НаименованиеВВерхнемРегистре";
В этом случае СУБД не сможет использовать индекс по полю Наименование, так как она обязана сначала применить функцию к каждой строке таблицы, а только потом сравнивать. Это приведет к полному сканированию таблицы (Table Scan). Поскольку запросы в 1С и так нечувствительны к регистру, дополнительные преобразования в секции ГДЕ излишни.
Иногда возникают ситуации, когда даже ВРег() не помогает. Выясним причину этого явления. Проблема может крыться в использовании символов из разных раскладок, которые выглядят идентично (например, латинская "C" и кириллическая "С").
Посмотрим на пример, который вернет Ложь, несмотря на визуальное сходство:
Строка1 = "С"; // Кириллица
Строка2 = "C"; // Латиница
Если ВРег(Строка1) = ВРег(Строка2) Тогда
// Сюда программа НЕ зайдет
КонецЕсли;
Для решения таких проблем при загрузке данных из внешних источников (Excel, CSV) рекомендуется предварительно обрабатывать строки, заменяя похожие латинские символы на кириллические или наоборот.
Также стоит учитывать особенности композитных символов в Юникоде. Один и тот же визуальный символ (например, "ё") может быть представлен в памяти либо как один код, либо как комбинация буквы "е" и диакритического знака "двоеточие сверху". С точки зрения 1С это будут разные строки. В таких случаях может потребоваться нормализация строк, хотя в стандартных бизнес-задачах это встречается редко.
В ходе обсуждения важно также затронуть тему сравнения неопределенных значений, так как это часто идет рука об руку со сравнением строк. Во встроенном языке 1С NULL = NULL всегда вернет Истина. Однако в языке запросов (SQL) результат любого сравнения с NULL (даже NULL = NULL) дает результат UNKNOWN, что в логике фильтрации приравнивается к Ложь.
Для проверки на пустое значение в запросах всегда используйте конструкцию ЕСТЬ NULL или IS NULL, вместо обычного равенства.
Для того чтобы ваш код работал стабильно и предсказуемо, мы рекомендуем придерживаться следующих правил:
ВРег(), НРег() или, что предпочтительнее для новых версий, СтрСравнить().ГДЕ, чтобы не терять производительность индексов.НайтиПоНаименованию()) обычно нечувствителен к регистру, что является исключением из общих правил встроенного языка.Таким образом, понимание архитектуры 1С и механизмов работы используемой СУБД позволяет избежать досадных ошибок "исчезающих данных" и писать надежный программный код.