Почему в 1С сравнение символов строки не совпадает со сравнением их кодов?

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

При разработке алгоритмов сортировки, парсинга или валидации строк разработчики часто сталкиваются с неочевидным поведением операторов сравнения во встроенном языке 1С. Рассмотрим классический парадокс: если проверить числовые коды символов точки и звёздочки, то код точки оказывается больше кода звёздочки, однако прямое сравнение строковых значений возвращает противоположный результат.

Посмотрим на пример кода, демонстрирующий эту ситуацию:


КодТочки = КодСимвола("."); // Возвращает 46
КодЗвездочки = КодСимвола("*"); // Возвращает 42

РезультатКодов = (КодТочки < КодЗвездочки); // 46 < 42 -> Ложь
РезультатСтрок = ("." < "*"); // Результат -> Истина

Разберем детально, почему так происходит, как платформа 1С обрабатывает текстовые данные и как реализовать строгое упорядочивание символов по кодам Unicode.

Причина различия: числовые коды против лингвистического сравнения

Проанализируем механику работы каждого оператора, чтобы понять разницу:

  1. Функция КодСимвола() возвращает целочисленный порядковый номер символа в таблице кодировки Unicode (UTF-16). Для символа точки "." этот код равен 46, а для знака умножения / звёздочки "*" — 42. Сравнение обычных чисел подчиняется строгим математическим правилам: 46 < 42 возвращает значение Ложь.
  2. Строковые операторы <, >, =, <=, >= выполняют не побайтовое и не числовое сопоставление символов, а лингвистическое сравнение (Collation). При строковом сравнении платформа сопоставляет символам весовые коэффициенты в соответствии с правилами Unicode Collation Algorithm (UCA) и системными языковыми стандартами.

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

Аналогичная ситуация возникает и с буквами алфавита. Например, выражение "а" < "Я" возвращает Истина, хотя в таблице кодировки заглавные буквы имеют меньший числовой код, чем строчные. Другой яркий пример — буква "ё": в таблице Unicode она расположена отдельно от основного диапазона русского алфавита, но при языковом сравнении корректно встает рядом с буквой "е".

Особенности сравнения строк в памяти и в базе данных

Разберем, как контекст выполнения влияет на работу со строками:

  1. Во встроенном языке (на клиенте и на сервере): операции сравнения строк выполняются в оперативной памяти. Стандартные операторы чувствительны к регистру (Case-Sensitive), но используют системные локальные таблицы весов операционной системы (Windows NLS или библиотека ICU в Linux). Функция глобального контекста СтрСравнить() и объект СравнениеЗначений также выполняют лингвистическое сопоставление, но без учета регистра.
  2. В запросах к СУБД: сортировка предложений УПОРЯДОЧИТЬ ПО и условия фильтрации ГДЕ выполняются силами базы данных. Здесь действуют правила Collation конкретной СУБД (например, Russian_100_CI_AS для MS SQL Server или внутренний Collation файла базы 1CD), где регистр букв по умолчанию игнорируется.

Важно помнить, что во встроенном языке платформы 1С нет штатного модификатора для операторов сравнения, аналогичного порядковому (Ordinal) режиму в других языках программирования.

Решение 1: Посимвольное сравнение по кодам символов

Если бизнес-логика алгоритма требует строгого лексикографического порядка (в точном соответствии с таблицей Unicode), напишем универсальную функцию для посимвольного сравнения кодов:


Функция СтрокаМеньшеПоКодам(Знач Строка1, Знач Строка2)
    Длина1 = СтрДлина(Строка1);
    Длина2 = СтрДлина(Строка2);
    МинДлина = Мин(Длина1, Длина2);
    
    Для Индекс = 1 По МинДлина Цикл
        Код1 = КодСимвола(Сред(Строка1, Индекс, 1));
        Код2 = КодСимвола(Сред(Строка2, Индекс, 1));
        
        Если Код1 <> Код2 Тогда
            Возврат Код1 < Код2;
        КонецЕсли;
    КонецЦикла;
    
    // Если общая часть совпала, меньшей считается более короткая строка
    Возврат Длина1 < Длина2;
КонецФункции

Посмотрим на результат работы этой функции применительно к нашей задаче:


Результат = СтрокаМеньшеПоКодам(".", "*");
// Функция вернет Ложь, так как КодСимвола(".") = 46, а КодСимвола("*") = 42

Решение 2: Сравнение через двоичные данные

Рассмотрим альтернативный способ, который подходит для сопоставления строк без явных циклов по каждому символу. Для этого переведем строки в двоичные данные с фиксированной кодировкой UTF-16LE, а затем получим шестнадцатеричную строку:


Функция ПолучитьHexСтроки(Знач ВходнаяСтрока)
    Двоичные = ПолучитьДвоичныеДанныеИзСтроки(ВходнаяСтрока, "UTF-16LE", Ложь);
    Возврат ВРег(ПолучитьHexСтрокуИзДвоичныхДанных(Двоичные));
КонецФункции

Шестнадцатеричное представление байтов состоит исключительно из цифр 0-9 и латинских букв A-F. Для таких нормализованных строк одинаковой разрядности стандартное сравнение строк полностью совпадает с фактическим порядком байтов в памяти компьютера.

← На главную