Перевод чисел между различными системами счисления — это классическая задача программирования, которая в среде 1С:Предприятие имеет свои нюансы. Особенно часто трудности возникают при работе с двоичными данными, где важна фиксированная длина строки, например, при генерации криптографических ключей, работе с хешами или протоколами обмена данными. Основная проблема заключается в том, что при использовании стандартных математических функций или приведении к типу Число, система автоматически отбрасывает «незначащие» лидирующие нули, что ломает структуру данных.
В этой статье мы подробно разберем, почему стандартный подход через промежуточное десятичное число не всегда подходит, и рассмотрим несколько эффективных способов решения задачи, включая работу с тетрадами и использование встроенных объектов платформы.
Рассмотрим стандартную логику, которую часто применяют начинающие разработчики: сначала двоичная строка переводится в десятичное число, а затем это число конвертируется в шестнадцатеричное представление. На первый взгляд, это кажется логичным, но давайте проанализируем ситуацию. В 1С тип Число является математическим объектом. Для математики числа "0001" и "1" идентичны. Поэтому, как только вы преобразуете строку "00001010" в число, 1С сохранит его как 10. При обратном преобразовании в Hex (шестнадцатеричную систему) вы получите "A" вместо ожидаемого "0A".
Если ваша задача требует, чтобы на выходе всегда было, например, 32 символа (как для 128-битного ключа), то потеря хотя бы одного нуля приведет к тому, что результат станет невалидным для принимающей стороны. Кроме того, при работе со сверхдлинными двоичными строками (более 64 бит) стандартный тип Число в 1С вызовет ошибку переполнения, так как он ограничен 64-битной разрядностью.
Самый простой способ, если вы все же используете математический перевод для небольших чисел — это принудительное дополнение итоговой строки нулями слева до нужной длины. Выясним, как это реализовать наиболее лаконично.
Допустим, мы получили шестнадцатеричное число HexРезультат, и мы знаем, что оно должно состоять из 32 символов. Мы можем воспользоваться строковыми функциями 1С:
// Предположим, HexРезультат уже получен и имеет длину меньше 32
НужнаяДлина = 32;
Если СтрДлина(HexРезультат) < НужнаяДлина Тогда
// Добавляем недостающее количество нулей в начало
НедостающееКолВо = НужнаяДлина - СтрДлина(HexРезультат);
Для Индекс = 1 По НедостающееКолВо Цикл
HexРезультат = "0" + HexРезультат;
КонецЦикла;
КонецЕсли;
Более элегантный и быстрый способ без цикла — использование функции Прав(). Проанализируем пример ниже:
// Создаем строку из нулей нужной длины и прибавляем к ней наш результат
// Затем берем правые N символов
НужнаяДлина = 32;
СтрокаШаблон = "00000000000000000000000000000000"; // 32 нуля
РезультатСНулями = Прав(СтрокаШаблон + HexРезультат, НужнаяДлина);
Этот метод хорош для простых задач, но он не решает проблему ограничения разрядности типа Число.
Разберем фундаментальный подход, который идеально подходит для работы с любыми длинами строк (хоть 256, хоть 512 бит) и гарантирует сохранение всех нулей. Основной принцип заключается в том, что каждые 4 бита двоичного числа в точности соответствуют 1 символу шестнадцатеричного числа. Такие четверки бит называются тетрадами.
Рассмотрим алгоритм по шагам:
Проанализируем пример функции, реализующей этот подход:
Функция ДвоичныйВHex(Знач ДвоичнаяСтрока) Экспорт
// 1. Приводим длину к кратной 4
Остаток = СтрДлина(ДвоичнаяСтрока) % 4;
Если Остаток > 0 Тогда
ДвоичнаяСтрока = Прав("000", 4 - Остаток) + ДвоичнаяСтрока;
КонецЕсли;
// 2. Создаем таблицу соответствия тетрад
Соответствие = Новый Соответствие;
Соответствие.Вставить("0000", "0"); Соответствие.Вставить("0001", "1");
Соответствие.Вставить("0010", "2"); Соответствие.Вставить("0011", "3");
Соответствие.Вставить("0100", "4"); Соответствие.Вставить("0101", "5");
Соответствие.Вставить("0110", "6"); Соответствие.Вставить("0111", "7");
Соответствие.Вставить("1000", "8"); Соответствие.Вставить("1001", "9");
Соответствие.Вставить("1010", "A"); Соответствие.Вставить("1011", "B");
Соответствие.Вставить("1100", "C"); Соответствие.Вставить("1101", "D");
Соответствие.Вставить("1110", "E"); Соответствие.Вставить("1111", "F");
Результат = "";
// 3. Обрабатываем строку по 4 символа
Для Сч = 1 По СтрДлина(ДвоичнаяСтрока) / 4 Цикл
Тетрада = Сред(ДвоичнаяСтрока, (Сч-1)*4 + 1, 4);
Результат = Результат + Соответствие.Получить(Тетрада);
КонецЦикла;
Возврат Результат;
КонецФункции
Преимущество данного метода: Мы вообще не используем арифметические операции умножения или деления. Мы работаем только со строками, поэтому этот метод никогда не выдаст ошибку «Недопустимое значение числа» и будет работать с данными любой длины.
В современных версиях платформы 1С появились мощные инструменты для работы с двоичными данными. Если ваше число укладывается в 64 бита, можно использовать функции преобразования строк и работу с потоками. Посмотрим, как это сделать через объекты ДвоичныеДанные.
Рассмотрим ситуацию: у нас есть двоичная строка, и мы хотим получить её Hex-представление через встроенный метод ПолучитьHexСтрокуИзДвоичныхДанных(). Этот метод уникален тем, что он автоматически сохраняет правильный формат.
// Пример перевода через объект ДвоичныеДанные
// Внимание: подходит, если число не превышает предел 64-бит
ДвоичнаяСтрока = "0000101000101011"; // Пример
Число10 = ЧислоИзДвоичнойСтроки("0b" + ДвоичнаяСтрока);
Поток = Новый ПотокВПамяти();
Запись = Новый ЗаписьДанных(Поток);
// Записываем как 64-битное целое
Запись.ЗаписатьЦелое64(Число10, ПорядокБайтов.BigEndian);
Запись.Закрыть();
ДД = Поток.ЗакрытьИПолучитьДвоичныеДанные();
HexСтрока = ПолучитьHexСтрокуИзДвоичныхДанных(ДД);
// На выходе получим полную строку с нулями
Сообщить(HexСтрока);
Важное примечание: Если вы работаете с задачами блокчейна (например, Bitcoin seed-фразы), как упоминалось в обсуждении, помните, что лимит ЗаписатьЦелое64 — это $2^{64}-1$. Для 256-битных ключей этот метод в чистом виде не сработает, и вам придется разбивать строку на байты (по 8 бит), записывать каждый байт отдельно в массив, и уже из массива создавать ДвоичныеДанные.
Проанализировав все способы, можно сделать следующие выводы:
Прав("000..." + Результат, Длина).Число, информация о лидирующих нулях теряется безвозвратно, и её придется восстанавливать вручную.Используя предложенные алгоритмы, вы сможете избежать ошибок при формировании сложных строковых идентификаторов и обеспечите корректную передачу данных в смежные системы.