как преобразовать массив байтов в его шестнадцатеричное представление в Delphi

У меня есть переменная TBytes со значением [0,0,15,15]. Как я могу преобразовать его в «00FF»?

Я не хочу использовать циклы, потому что эта логика будет использоваться во время интенсивной функции.

(Я пытался использовать BinToHex, но не смог заставить его работать со строковой переменной.)

С уважением,

Паван.


person Pavan    schedule 29.06.2009    source источник


Ответы (4)


// Swapping is necessary because x86 is little-endian.
function Swap32(value: Integer): Integer;
asm
  bswap eax
end;

function FourBytesToHex(const bytes: TBytes): string;
var
  IntBytes: PInteger;
  FullResult: string;
begin
  Assert(Length(bytes) = SizeOf(IntBytes^));
  IntBytes := PInteger(bytes);
  FullResult := IntToHex(Swap32(IntBytes^), 8);
  Result := FullResult[2] + FullResult[4] + FullResult[6] + FullResult[8];
end;

Если эта последняя строка выглядит немного странно, то это потому, что вы запросили преобразование четырехбайтового массива в четырехсимвольную строку, тогда как в общем случае для представления четырехзначного числа требуется восемь шестнадцатеричных цифр. значение байта. Я просто предполагаю, что все ваши значения байтов ниже 16, поэтому требуется только одна шестнадцатеричная цифра. Если в вашем примере была опечатка, то просто замените две последние строки на эту:

Result := IntToHex(Swap32(IntBytes^), 8);

Кстати, ваше требование о запрете петель не будет выполнено. IntToHex использует внутренний цикл.

person Rob Kennedy    schedule 29.06.2009
comment
IntToHex(IntBytes^, 8); генерирует строку в обратном порядке. Если вы видите мой вопрос, мне нужно, чтобы он был 00FF, но вышеприведенная функция генерирует его как FF00. Могу ли я указать порядок байтов? - person Pavan; 30.06.2009
comment
Результат := ПолныйРезультат[8] + ПолныйРезультат[6] + ПолныйРезультат[4] + ПолныйРезультат[2]; - person Nosredna; 30.06.2009
comment
есть ли способ указать порядок байтов вместо доступа к нему поэлементно..?? - person Pavan; 30.06.2009
comment
Извиняюсь. Я забыл, что x86 имеет прямой порядок следования байтов, поэтому вам нужно поменять местами порядок байтов. - person Rob Kennedy; 30.06.2009
comment
@Pavan: не могли бы вы просто добавить логический параметр порядка байтов в заголовок функции, а затем использовать последнюю строку из исходного ответа Роба и комментарий Носредны в качестве альтернатив в операторе if? - person Argalatyr; 30.06.2009

Это довольно быстро и работает с любым размером массива. Это похоже на BinToHex, но вместо того, чтобы ожидать значений 0..255 байт, он использует только младший полубайт.

procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);
const
  Convert: array[0..15] of AnsiChar = '0123456789ABCDEF';
var
  I: Integer;
begin
  for I := 0 to BufSize - 1 do
  begin
    Text[0] := Convert[Byte(Buffer[I]) and $F];
    Inc(Text);
  end;
end;

Ассемблер, который делает то же самое:

procedure BinToSingleHex(Buffer, Text: PAnsiChar; BufSize: Integer);assembler;
asm
        PUSH    ESI
        PUSH    EDI
        MOV     ESI,EAX
        MOV     EDI,EDX
        MOV     EDX,0
        JMP     @@1
@@0:    DB      '0123456789ABCDEF'
@@1:    LODSB
        AND     DL,AL
        AND     DL,0FH
        MOV     AL,@@0.Byte[EDX]
        STOSB
        DEC     ECX
        JNE     @@1
        POP     EDI
        POP     ESI
end;

Применение:

type  THexDigit=0..15;
const ArSize=16;
var   Ar:array[0..Pred(ArSize)] of THexDigit=(0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3);
      S:Array[0..Pred(ArSize)] of AnsiChar;

BinToSingleHex(@Ar,S,Length(Ar));
WriteLn(S);
person Wouter van Nifterick    schedule 29.06.2009
comment
Остерегаться. Ваша строка результата не завершается нулем. Кроме того, ваш код требует, чтобы директива компилятора typed @ operator была отключена, поскольку вы передаете указатель байта в параметр, ожидающий указатель AnsiChar. Лучше изменить тип первого параметра на PByte. И как вы понимаете, что вам нужно использовать и очищать старшие четыре бита в Delphi, но вам не нужно в ассемблере? - person Rob Kennedy; 30.06.2009
comment
Хорошие моменты ... и да, вы правы ... конечно, все еще необходимо замаскировать самые значимые 4 бита. исправил это - person Wouter van Nifterick; 30.06.2009

Немного поздно для вечеринки, но почему бы не использовать простую таблицу поиска?

const
HexChars : Array[0..15] of Char = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');

Предполагая значения TBytes 0..15

Function (ABytea: TBytes): string
    begin 
      Result  := HexChars[ABytea[0]];
      Result  := Result + HexChars[ABytea[1]];
      Result  := Result + HexChars[ABytea[2]];
      Result  := Result + HexChars[ABytea[3]];
    end;

конечно, лучше с циклом :) и нуждается в изменении для значений байтов выше 15:

begin 
  Result  := HexChars[ABytea[0] shr 4];
  Result  := Result + HexChars[ABytea[0] and $0F];
  Result  := Result + HexChars[ABytea[1] shr 4];
  Result  := Result + HexChars[ABytea[1] and $0F];
  Result  := Result + HexChars[ABytea[2] shr 4];
  Result  := Result + HexChars[ABytea[2] and $0F];
  Result  := Result + HexChars[ABytea[3] shr 4];
  Result  := Result + HexChars[ABytea[3] and $0F];
end;

Еще лучше с циклом, особенно если ТБ становится больше.

person Despatcher    schedule 02.07.2009

person    schedule
comment
это терабайты, а не один байт... и я не хочу использовать циклы для вычисления каждого значения. Если я не ошибаюсь, ваша логика работает на байт. - person Pavan; 30.06.2009
comment
Это похоже на лучшее решение, которое вы собираетесь получить. Где-то вам придется использовать петлю. Если вы хотите использовать его на терабайтах, оберните его в функцию, которая берет терабайты и перебирает их, формируя выходную строку по одному байту за раз. - person Mason Wheeler; 30.06.2009
comment
Это может быть адаптировано к решению без цикла. Если вы действительно заботитесь об этом требовании. - person Nosredna; 30.06.2009