В процессе разработки часто возникает необходимость преобразования данных между различными системами счисления. Особенно это актуально при работе с низкоуровневыми протоколами торгового оборудования, анализе штрихкодов или криптографических операциях. Одной из наиболее частых задач является конвертация из шестнадцатеричной (Hex) системы в двоичную (Binary). В этой статье мы подробно разберем, почему возникают ошибки при стандартном подходе и как реализовать надежный алгоритм на языке 1С.
Прежде чем приступать к написанию кода, разберем теоретическую базу. Шестнадцатеричная система счисления имеет основание 16 (2 в четвертой степени). Это означает, что каждой цифре шестнадцатеричного числа (от 0 до F) соответствует ровно четыре знака (бита) в двоичной системе. Такая группа из 4 бит называется тетрадой.
Рассмотрим таблицу соответствия:
Именно благодаря этому свойству перевод из Hex в Binary можно выполнять «в лоб» — просто заменяя каждый символ исходной строки соответствующей четверкой нулей и единиц. Это гораздо эффективнее и точнее, чем использование промежуточного преобразования в десятичное число.
Многие начинающие программисты пытаются решить задачу через перевод Hex -> Dec -> Bin. Проанализируем ситуацию, когда этот метод дает сбой. В 1С тип Число ограничен 38 знаками. Если вам нужно перевести длинный идентификатор или GUID (например, 128-битный код), промежуточное десятичное число просто «не влезет» в переменную или потеряет точность. Кроме того, при математическом переводе часто теряются лидирующие нули, что критично для двоичных данных фиксированной длины.
Наиболее надежный способ для работы со строками любой длины — это перебор каждого символа шестнадцатеричной строки. Рассмотрим пример функции, которая реализует этот алгоритм через оператор Если...ИначеЕсли. Этот метод гарантирует сохранение всех бит, включая ведущие нули каждой тетрады.
Функция ШестнадцатиричноеВДвоичное(Знач ВходнаяСтрока) Экспорт
Стр = СокрЛП(ВходнаяСтрока);
Результат = "";
Для н = 1 По СтрДлина(Стр) Цикл
Буква = Вверх(Сред(Стр, н, 1));
Если Буква = "0" Тогда Результат = Результат + "0000";
ИначеЕсли Буква = "1" Тогда Результат = Результат + "0001";
ИначеЕсли Буква = "2" Тогда Результат = Результат + "0010";
ИначеЕсли Буква = "3" Тогда Результат = Результат + "0011";
ИначеЕсли Буква = "4" Тогда Результат = Результат + "0100";
ИначеЕсли Буква = "5" Тогда Результат = Результат + "0101";
ИначеЕсли Буква = "6" Тогда Результат = Результат + "0110";
ИначеЕсли Буква = "7" Тогда Результат = Результат + "0111";
ИначеЕсли Буква = "8" Тогда Результат = Результат + "1000";
ИначеЕсли Буква = "9" Тогда Результат = Результат + "1001";
ИначеЕсли Буква = "A" Тогда Результат = Результат + "1010";
ИначеЕсли Буква = "B" Тогда Результат = Результат + "1011";
ИначеЕсли Буква = "C" Тогда Результат = Результат + "1100";
ИначеЕсли Буква = "D" Тогда Результат = Результат + "1101";
ИначеЕсли Буква = "E" Тогда Результат = Результат + "1110";
ИначеЕсли Буква = "F" Тогда Результат = Результат + "1111";
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Важный момент: если вы используете результат для вычислений, вам может потребоваться убрать лишние нули в самом начале итоговой строки. Однако для передачи данных в оборудование обычно требуется именно полная строка с сохранением разрядности.
Если планируется обрабатывать большие объемы данных, постоянное выполнение условий ИначеЕсли может замедлить работу. Проанализируем более изящный способ — использование объекта Соответствие. Мы один раз инициализируем таблицу перевода в памяти, а затем мгновенно получаем значение по ключу.
Функция HexToBinaryFast(Знач СтрHex) Экспорт
ТаблицаТетрад = Новый Соответствие;
ТаблицаТетрад.Вставить("0", "0000"); ТаблицаТетрад.Вставить("1", "0001");
ТаблицаТетрад.Вставить("2", "0010"); ТаблицаТетрад.Вставить("3", "0011");
ТаблицаТетрад.Вставить("4", "0100"); ТаблицаТетрад.Вставить("5", "0101");
ТаблицаТетрад.Вставить("6", "0110"); ТаблицаТетрад.Вставить("7", "0111");
ТаблицаТетрад.Вставить("8", "1000"); ТаблицаТетрад.Вставить("9", "1001");
ТаблицаТетрад.Вставить("A", "1010"); ТаблицаТетрад.Вставить("B", "1011");
ТаблицаТетрад.Вставить("C", "1100"); ТаблицаТетрад.Вставить("D", "1101");
ТаблицаТетрад.Вставить("E", "1110"); ТаблицаТетрад.Вставить("F", "1111");
Результат = "";
Для Позиция = 1 По СтрДлина(СтрHex) Цикл
Символ = Вверх(Сред(СтрHex, Позиция, 1));
ДвоичныйЭквивалент = ТаблицаТетрад.Получить(Символ);
Если ДвоичныйЭквивалент <> Неопределено Тогда
Результат = Результат + ДвоичныйЭквивалент;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Выясним ситуацию, когда нам всё же нужно работать через десятичное число. Это удобно, если число гарантированно умещается в стандартный тип Число. В современных версиях платформы 1С (8.3.10+) появились встроенные функции ЧислоИзШестнадцатеричнойСтроки и ПолучитьДвоичнуюСтрокуИзЧисла. Посмотрим, как их скомбинировать.
Функция КонвертироватьЧерезПлатформу(Знач HexСтрока)
// Преобразуем Hex в десятичное число
ДесятичноеЧисло = ЧислоИзШестнадцатеричнойСтроки(HexСтрока);
// Преобразуем число в двоичную строку
// Второй параметр позволяет задать минимальную длину строки (например, 8, 16, 32)
Возврат ПолучитьДвоичнуюСтрокуИзЧисла(ДесятичноеЧисло);
КонецФункции
Обратите внимание: встроенная функция ПолучитьДвоичнуюСтрокуИзЧисла по умолчанию не добавляет ведущие нули до тетрад, она возвращает минимально необходимую строку. Если вам нужно строгое соответствие 1 символ Hex = 4 символа Bin, используйте первые два метода.
Разберем основные грабли, на которые наступают разработчики:
Вверх() или НРег()/ВРег() перед поиском.0x0F в двоичной системе — это 00001111. Математический метод может вернуть просто 1111, потеряв первые четыре нуля. Это критично, если вы формируете пакет данных для отправки по TCP/IP или в COM-порт.СокрЛП() перед обработкой.Подводя итог, можно сказать, что для большинства задач в 1С наиболее предпочтительным является метод посимвольной замены (Решение №1 или №2). Он прост в отладке, не зависит от разрядности платформы и гарантирует побитовую точность преобразования.