Delphi. Массив байтов в строку шестнадцатеричных значений.
Иногда бывает нужно посмотреть значение массива байтов в понятном человеку виде, чаще всего в виде пар шестнадцатеричных цифр. Задача может быть решена простой функцией, на примере которой мы увидим три подхода к работе со строками в Delphi и Паскале.
Паскалевские строки: наглядно и просто
Предлагаемый ниже способ хотя и не максимально быстрый, но близок к нему, обладая при этом наглядностью и компактностью. Наиболее быстрым решением, видимо, будет определение постоянного массива символов от Chr(0) до Chr(255) и адресация по значению очередного байта в цикле.
function ByteToStr(bytes: array of byte): string;
const
BytesHex: array[0..15] of char =
('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
var
i, len: integer;
begin
len := Length(bytes);
SetLength(Result, len * 2);
for i := 0 to len - 1 do begin
Result[i * 2 + 1] := BytesHex[bytes[i] shr 4];
Result[i * 2 + 2] := BytesHex[bytes[i] and $0F];
end;
end;
Пример использования
const
Bytes: array[0..11] of byte = (1, 2, 3, 4, 5, 10, 25, 35, 99, 122, 173, 255);
...
Writeln(ByteToStr(Bytes));
...
На экране видим: 01020304050A1923637AADFF
Функцию легко модифицировать, чтобы показывать значения в формате 0xDD или разделенные пробелом.
function ByteToStr(bytes: array of byte): string;
const
BytesHex: array[0..15] of char =
('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
var
i, len: integer;
begin
len := Length(bytes);
SetLength(Result, len * 5);
for i := 0 to len - 1 do begin
Result[i * 5 + 1] := '0';
Result[i * 5 + 2] := 'x';
Result[i * 5 + 3] := BytesHex[bytes[i] shr 4];
Result[i * 5 + 4] := BytesHex[bytes[i] and $0F];
Result[i * 5 + 5] := ' ';
end;
end;
Результат: 0x01 0x02 0x03 0x04 0x05 0x0A 0x19 0x23 0x63 0x7A 0xAD 0xFF
"Сишные" строки: менее наглядно, но быстрее
В предыдущем примере мы использовали паскалевские строки, хотя известно, что управление ими менее эффективно, чем, например, прямые манипуляции указателями PChar в сишном стиле. Например, если открыть дизассемблер, то при каждом обращении к строке будет вызываться функция UniqueString. Это добавит несколько тактов процессора на каждое такое присвоение.
Зато первый пример был наглядный. Не зря же Вирт придумал язык! Поэтому приведем и более оптимальный по скорости, чтобы можно было сравнить.
function ByteToStr(bytes: array of byte): string;
const
BytesHex: array[0..15] of char =
('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
var
i, len: integer;
s: PChar;
begin
len := Length(bytes);
SetLength(Result, len * 5);
s := StrAlloc(len * 5 + 1);
for i := 0 to len - 1 do begin
StrLCopy(@s[i * 5], '0x', 2);
StrLCopy(@s[i * 5 + 2], @BytesHex[bytes[i] shr 4], 1);
StrLCopy(@s[i * 5 + 3], @BytesHex[bytes[i] and $0F], 1);
StrLCopy(@s[i * 5 + 4], ' ', 1);
end;
Result := s;
SysUtils.StrDispose(s);
end;
Прямое управление указателем в ассемблерном стиле: максимальная эффективность
function ByteToStr(bytes: array of byte): string;
const
BytesHex: array[0..15] of char =
('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
var
i, j, len: integer;
s: PChar;
begin
len := Length(bytes);
SetLength(Result, len * 5);
s := StrAlloc(len * 5 + 1);
j := 0;
for i := 0 to len - 1 do begin
s[j] := '0';
Inc(j);
s[j] := 'x';
Inc(j);
s[j] := BytesHex[bytes[i] shr 4];
Inc(j);
s[j] := BytesHex[bytes[i] and $0F];
Inc(j);
s[j] := ' ';
Inc(j);
end;
Result := s;
SysUtils.StrDispose(s);
end;
blog comments powered by Disqus