Превращаем байты в строки и обратно: магия шестнадцатеричного кода в C#
Вы когда-нибудь задумывались, как компьютер видит картинку, музыку или просто текстовый файл? Всё это — всего лишь последовательность байтов. Но байты не очень удобно показывать пользователю или передавать в URL. Тут на помощь приходит шестнадцатеричная строка (hex string) — компактное и читаемое представление двоичных данных.
Представьте: вы пишете криптографический модуль, работаете с хешами, передаёте бинарные данные через JSON или логируете содержимое пакета. И тут — бац! — вам нужно превратить массив байтов в понятную строку. Или наоборот, из hex-строки восстановить исходные байты. Звучит знакомо? Этот вопрос — один из самых популярных на Stack Overflow, и сегодня мы разберём его раз и навсегда. Добро пожаловать на pythonlib.ru — ваш гид в мире программирования.
Зачем вообще конвертировать byte array в hex строку?
Давайте сразу к делу. Байтовый массив (byte[]) — это сырые данные: числа от 0 до 255. Человеку их читать сложно, а в некоторых текстовых протоколах (HTTP, JSON) они вообще могут сломать формат. Hex-строка решает обе проблемы:
- Компактность: один байт превращается в два символа (например, 255 → "FF").
- Безопасность: в строке остаются только безопасные символы (0-9, A-F).
- Удобство: hex-строки легко копировать, сравнивать и логировать.
Вы наверняка видели такие строки в хешах MD5, SHA256 или в токенах авторизации. Теперь вы знаете, как это работает «под капотом».
Метод №1: Классика — BitConverter (просто и быстро)
Самый популярный способ в C# — использовать встроенный класс BitConverter. Он есть в .NET с самой первой версии и работает без лишних заморочек.
Преобразование byte[] → hex string
byte[] data = { 0x0F, 0x1A, 0xFF, 0x00 };
string hexString = BitConverter.ToString(data);
Console.WriteLine(hexString); // Вывод: 0F-1A-FF-00
Всё просто, правда? Но есть нюанс: между парами символов вставляется дефис. Если дефис вам мешает, его легко убрать:
string hexString = BitConverter.ToString(data).Replace("-", "");
Console.WriteLine(hexString); // Вывод: 0F1AFF00
Плюсы: минимум кода, встроенный метод, понятный новичкам.
Минусы: лишний дефис (нужно удалять), небольшое выделение памяти на промежуточные строки.
Обратное преобразование: hex string → byte[]
А вот с обратной операцией чуть сложнее. BitConverter умеет только в одну сторону. Но мы не сдаёмся — напишем простой парсер:
public static byte[] HexStringToBytes(string hex)
{
// Убираем возможные дефисы и пробелы
hex = hex.Replace("-", "").Replace(" ", "");
int length = hex.Length;
byte[] bytes = new byte[length / 2];
for (int i = 0; i < length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
// Использование:
string hex = "0F1AFF00";
byte[] result = HexStringToBytes(hex);
// result: { 0x0F, 0x1A, 0xFF, 0x00 }
Этот метод использует Convert.ToByte с базой 16 — надёжно и читаемо. Но для больших массивов Substring создаёт много временных строк. Для production-кода лучше использовать Span<T> или цикл с индексацией символов.
Метод №2: Современный — Convert.ToHexString (.NET 5+)
Начиная с .NET 5, Microsoft добавила долгожданный метод Convert.ToHexString. Он работает быстро и без дефисов.
byte[] data = { 0x0F, 0x1A, 0xFF, 0x00 };
string hexString = Convert.ToHexString(data);
Console.WriteLine(hexString); // Вывод: 0F1AFF00
Красота! Никаких дефисов, никаких Replace. А для обратной операции есть Convert.FromHexString:
string hex = "0F1AFF00";
byte[] bytes = Convert.FromHexString(hex);
// bytes: { 0x0F, 0x1A, 0xFF, 0x00 }
Важно: этот метод доступен только в .NET 5 и выше, а также в .NET Core 3.0+ (с некоторыми ограничениями). Если вы работаете на старых версиях (например, .NET Framework 4.x), используйте BitConverter.
Метод №3: Ручная конвертация — полный контроль (для гиков)
Иногда нужно понимать, что происходит «под капотом». Или вы хотите максимальную производительность без выделения лишней памяти. Тогда пишем всё сами.
Byte[] → Hex string (ручной способ)
public static string BytesToHexManual(byte[] bytes)
{
char[] hexChars = new char[bytes.Length * 2];
for (int i = 0; i < bytes.Length; i++)
{
int value = bytes[i];
// Старший полубайт
hexChars[i * 2] = "0123456789ABCDEF"[value >> 4];
// Младший полубайт
hexChars[i * 2 + 1] = "0123456789ABCDEF"[value & 0x0F];
}
return new string(hexChars);
}
// Использование:
byte[] data = { 0x0F, 0x1A, 0xFF, 0x00 };
string hex = BytesToHexManual(data);
Console.WriteLine(hex); // 0F1AFF00
Здесь мы используем массив символов и таблицу "0123456789ABCDEF" для быстрой конвертации. Никаких Substring или Convert.ToString — только арифметика и индексация.
Hex string → Byte[] (ручной способ)
public static byte[] HexToBytesManual(string hex)
{
if (hex.Length % 2 != 0)
throw new ArgumentException("Длина hex-строки должна быть чётной");
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
int high = HexCharToInt(hex[i * 2]);
int low = HexCharToInt(hex[i * 2 + 1]);
bytes[i] = (byte)((high << 4) | low);
}
return bytes;
}
private static int HexCharToInt(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if