Как перевести ГУИД в число и обратно в 1С 8.3?

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

В процессе разработки интеграционных механизмов, распределения данных по параллельным потокам обмена или формирования коротких идентификаторов нередко возникает задача взаимной конвертации уникального идентификатора (GUID / UUID) в числовое значение и обратно. Разберем по шагам, как устроен этот механизм, с какими подводными камнями можно столкнуться при реализации алгоритма во встроенном языке 1С и как выглядит готовое решение.

В чем заключается назначение функции ДобавитьНачСимвол

При изучении готовых примеров перевода шестнадцатеричной строки в число и обратно разработчики часто встречают вызов вспомогательной функции вида бфСтроки.ДобавитьНачСимвол(СтрокаЧ16, 32, "0"). Выясним причину ее обязательного присутствия.

Стандартный строковый GUID без дефисов представляет собой 128-битное число, записанное в шестнадцатеричной нотации и состоящее ровно из 32 символов (по 2 символа на каждый из 16 байт). Когда мы переводим такое число из десятичной системы обратно в шестнадцатеричную путем деления на основание системы (16), старшие незначащие нули отбрасываются. Если исходный идентификатор имел нули в начале старших разрядов, результат обратного перевода вернет строку длиной менее 32 символов (например, 30 или 31 символ).

Если после этого попытаться собрать GUID стандартным разбиением по позициям через Сред(), все смещения байтов нарушатся, а платформа выдаст ошибку при создании объекта Новый УникальныйИдентификатор. Функция дополнения символов слева как раз гарантирует, что строка всегда будет иметь фиксированную длину в 32 символа за счет добавления ведущих нулей.

Подводные камни длинной арифметики и регистра символов

Проанализируем типичные ошибки, возникающие при написании собственных функций конвертации:

  1. Регистр букв шестнадцатеричной строки. Метод платформы Строка(Новый УникальныйИдентификатор) возвращает шестнадцатеричные символы в нижнем регистре (a-f). Если в таблице подстановки используются заглавные буквы (A-F), поиск позиции символа через функцию Найти() вернет 0, что полностью исказит результат. Обязательно используем функцию ВРег() при обработке строкового идентификатора.
  2. Ограничения типа Число в метаданных. Важно помнить, что максимальное 128-битное шестнадцатеричное число в десятичном виде содержит 39 разрядов. Во встроенном языке 1С в оперативной памяти тип Число поддерживает вычисления с такой разрядностью, однако сохранить полученное 39-значное число в реквизит справочника или ресурс регистра сведений штатными средствами невозможно, так как максимальная длина числового реквизита в метаданных ограничена 32 знаками.
  3. Порядок байтов (Endianness). Внутреннее бинарное хранение ссылки в СУБД (поле _IDRRef) использует смешанный порядок байтов (little-endian для первых трех групп). Если требуется получить числовое представление, в точности соответствующее внутреннему бинарному представлению ссылки в базе данных, строки первого, второго и третьего блоков необходимо переставлять парами байтов.

Готовый алгоритм прямого и обратного преобразования

Рассмотрим полный программный код функций, реализующих надежный перевод GUID в десятичное число и его корректное восстановление.


Функция Из_Любой_В_10(Значение, Нотация = 16) Экспорт

    Если Нотация <= 0 Тогда
        Возврат 0;
    КонецЕсли;

    Значение = СокрЛП(ВРег(Значение));
    Если Значение = "0" Или ПустаяСтрока(Значение) Тогда
        Возврат 0;
    КонецЕсли;

    Результат = 0;
    Символы = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    Длина = СтрДлина(Значение);

    Для Х = 1 По Длина Цикл
        М = 1;
        Для У = 1 По Длина - Х Цикл 
            М = М * Нотация;
        КонецЦикла;
        
        Позиция = Найти(Символы, Сред(Значение, Х, 1)) - 1;
        Результат = Результат + Позиция * М;
    КонецЦикла;

    Возврат Окр(Результат);

КонецФункции

Функция Из_10_В_Любую(Значение, Нотация = 16) Экспорт

    Если Нотация <= 0 Тогда 
        Возврат "";
    КонецЕсли;

    Значение = Число(Значение);
    Если Значение <= 0 Тогда 
        Возврат "0";
    КонецЕсли;

    Значение = Цел(Значение);
    Результат = "";
    Символы = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    Пока Значение > 0 Цикл
        Остаток = Значение % Нотация;
        Результат = Сред(Символы, Остаток + 1, 1) + Результат;
        Значение = Цел(Значение / Нотация);
    КонецЦикла;

    Возврат Результат;

КонецФункции

Функция ДополнитьСтрокуСлева(ИсходнаяСтрока, ТребуемаяДлина, СимволЗаполнитель = "0")
    
    Результат = ИсходнаяСтрока;
    Пока СтрДлина(Результат) < ТребуемаяДлина Цикл
        Результат = СимволЗаполнитель + Результат;
    КонецЦикла;
    
    Возврат Результат;
    
КонецФункции

Функция УникальныйИдентификаторВЧисло(GUID) Экспорт

    // Удаляем дефисы и приводим к верхнему регистру
    СтрокаGUID = СтрЗаменить(ВРег(Строка(GUID)), "-", "");
    
    // Перестановка байтов для соответствия порядку хранения в СУБД (1C / MS SQL)
    ч1 = Сред(СтрокаGUID, 1, 2);
    ч2 = Сред(СтрокаGUID, 3, 2);
    ч3 = Сред(СтрокаGUID, 5, 2);
    ч4 = Сред(СтрокаGUID, 7, 2);
    
    ч5 = Сред(СтрокаGUID, 9, 2);
    ч6 = Сред(СтрокаGUID, 11, 2);
    
    ч7 = Сред(СтрокаGUID, 13, 2);
    ч8 = Сред(СтрокаGUID, 15, 2);
    
    ч9 = Сред(СтрокаGUID, 17, 16);
    
    СтрGUID = ч4 + ч3 + ч2 + ч1 + ч6 + ч5 + ч8 + ч7 + ч9;
    
    Возврат Из_Любой_В_10(СтрGUID, 16);

КонецФункции

Функция УникальныйИдентификаторИзЧисла(ЧисловоеЗначение) Экспорт

    СтрокаGUID = Из_10_В_Любую(ЧисловоеЗначение, 16);
    // Дополняем ведущими нулями до 32 символов
    СтрокаGUID = ДополнитьСтрокуСлева(СтрокаGUID, 32, "0");
    
    // Обратная перестановка байтов
    ч1 = Сред(СтрокаGUID, 1, 2);
    ч2 = Сред(СтрокаGUID, 3, 2);
    ч3 = Сред(СтрокаGUID, 5, 2);
    ч4 = Сред(СтрокаGUID, 7, 2);
    
    ч5 = Сред(СтрокаGUID, 9, 2);
    ч6 = Сред(СтрокаGUID, 11, 2);
    
    ч7 = Сред(СтрокаGUID, 13, 2);
    ч8 = Сред(СтрокаGUID, 15, 2);
    
    ч9 = Сред(СтрокаGUID, 17, 4);
    ч10 = Сред(СтрокаGUID, 21, 12);
    
    СтрКаноническийGUID = ч4 + ч3 + ч2 + ч1 + "-" + ч6 + ч5 + "-" + ч8 + ч7 + "-" + ч9 + "-" + ч10;
    
    Возврат Новый УникальныйИдентификатор(СтрКаноническийGUID);

КонецФункции

Альтернативные и более производительные варианты

Разберем ситуации, когда перевод полного 128-битного GUID в число является избыточным. Если цель задачи — распределение объектов по корзинам или потокам (например, вычисление остатка по модулю для разделения на N потоков многопоточного обмена), длинная арифметика на циклах будет создавать существенную нагрузку на процессор.

В этом случае эффективнее применить следующие альтернативы:

  1. Расчет по хвосту идентификатора. Достаточно взять последние 4–6 шестнадцатеричных символов строки GUID. Число из диапазона до $16^4 = 65536$ или $16^6 \approx 16{,}7$ млн мгновенно переводится в десятичное число без нагрузки и делится с остатком на число потоков:
    
    ХвостGUID = Прав(СтрЗаменить(Строка(Ссылка.УникальныйИдентификатор()), "-", ""), 4);
    НомерПотока = (Из_Любой_В_10(ХвостGUID, 16) % КоличествоПотоков) + 1;
    
    Такой расчет полностью детерминирован: один и тот же объект всегда попадет в один и тот же поток.
  2. Использование типовых механизмов конфигураций. В типовых решениях (например, «Управление торговлей 11», ERP, КА) уже предусмотрены оптимизированные функции в общих модулях, такие как ЧисловойКодПоСсылке и ПолучитьСсылкуПоШтрихкодуТабличногоДокумента, предназначенные для формирования компактных числовых идентификаторов.
← На главную