变体字节数组到十六进制字符串

问题描述

我认为我有很大的知识差距需要跳过,但还没有解决问题。

在我的应用程序中,我使用 MS sql Server 自动创建特殊的 GUID。该列的认值为:

(CONVERT([binary](16),newid()))

导致类似的事情

0x0184F4422C58EA4A95B366E2F90F5973

一切正常,但有时我需要将该 Binary(16) 写入日志文件我有一个为我生成 GUID 的函数,可以随意使用。对于 sql 查询,我使用 FireDAC 查询函数看起来像这样:

function GetNewGUID: variant;
const
  cCREATENEWGUID = 'select (CONVERT([binary](16),newid())) as GUID';
var
  lquery: TFDQuery;
begin
  try
    lquery := TFDQuery.Create(nil);
    try
      lquery.Connection:=MyFDConnection;
      lquery.Open(cCREATENEWGUID);
      Result := lquery.FieldByName('GUID').AsVariant;
    finally
      lquery.Free;
    end;
  Except
    on e: Exception do
      // Some logging here
  end;
end;

这给了我完美工作的 Variant。但是当我喜欢将它显示为字符串时,我不明白。 我试过 vartostr,这给了我 ???? 我试过 TEncoding.ANSI.GetString,这给了我一些奇怪的字符

到目前为止,我的 Variant 是 Vararray 和数组字节类型。

解决方法

感谢 Olivier,他为我指出了正确的主题“以十六进制表示法获取 VarByte”。我找到了一个将 Var Byte Array 转换为 String 的解决方案。我就是这样解决的:

首先将您的 Variant 更改为 TBytes。使用找到的代码 here

Function VariantToBytes(Const Value: Variant): TBytes;
Var
  Size: Integer;
  pData: Pointer;
Begin
  Size := Succ(VarArrayHighBound(Value,1) - VarArrayLowBound(Value,1));
  SetLength(Result,Size);
  pData := VarArrayLock(Value);
  Try
    Move(pData^,Pointer(Result)^,Size);
  Finally
    VarArrayUnlock(Value);
  End;
End;

然后使用该函数将您的二进制文件更改为字符串:

function MyBinToHex(ABuf: PByte; ALen: Cardinal): string; overload;
const
  HexDigits: array[0..$F] of Char = '0123456789ABCDEF';
var
   i: Integer;
begin
   if ALen = 0 then
    Exit('');
   SetLength(Result,2 * ALen + 2);
   Result[1] := '0';
   Result[2] := 'x';
   hublogger.Debug(ALen.ToString);
   for i := 0 to ALen do
   begin
     Result[2*i + 3] := HexDigits[ABuf[i] shr 4];
     Result[2*i + 4] := HexDigits[ABuf[i] and $0F];
   end;
end;

function MyBinToHex(ABytes: TArray<Byte>): string; overload;
begin
  Result := MyBinToHex(PByte(ABytes),Length(ABytes));
end;

我的最终电话看起来像那样

var
 NewGUID : Variant;
 GUIDStr : String;
begin
 NewGUID := getNewGUID;
 GUIDStr := MyBinToHex(VariantToBytes(newgguid))

end;
,

这是将包含字节数组的变体转换为具有该数组的十六进制表示的字符串所需的代码。代码避免了不必要的数据复制:

function VarArrayOfByteToString(V : Variant) : String;
var
    P    : PByte;
    Size : Integer;
begin
    Result := '';
    P := VarArrayLock(V);
    if P <> nil then begin
        try
            Size := (VarArrayHighBound(V,1) -
                     VarArrayLowBound(V,1) + 1) *
                    TVarData(V).VArray^.ElementSize;
            while Size > 0 do begin
                Result := Result + IntToHex(P^,2);
                Inc(P);
                Dec(Size);
            end;
        finally
            VarArrayUnlock(V);
        end;
    end;
end;

使用示例:

procedure TForm21.Button1Click(Sender: TObject);
var
    V    : Variant;
    S    : String;
begin
    V := GetNewGUID;
    S := VarArrayOfByteToString(V);
    Memo1.Lines.Add(S);
end;

获得 GUID 的方法要快得多!代码是:

function CreateGuid : String;
var
    Guid   : TGUID;
begin
    if CoCreateGuid(Guid) <> 0 then   // Uses Winapi.Activex
        Guid := GUID_NULL;
    Result := GUIDToString(Guid);
end;

它以通常的格式以字符串形式返回一个 GUID:

{B9DAFFD0-87E1-4DA0-957B-DFF34CA047EB}

当然你可以去掉破折号和大括号:

S := CreateGUID;
Delete(S,38,1);
Delete(S,25,20,15,10,1,1);
,

据我所知,您问的是如何将变体(保存 16 字节数组,GUID)转换为十六进制字符串表示形式。如果您不介意,我还包括创建 GUID,因为它可以顺利融合。我相信我已经保持了与您现有代码的兼容性。

三个任务:

  • a) 创建 GUID

  • b) 将该 GUID 转换为变体

  • c) 将 GUID 转换为十六进制字符串

从创建 GUID,将其转换为字节数组(和变体),再到十六进制字符串的最终结果的完整过程如下:

var
  G: TGUID;
  B: TBytes;
  S: string;
  V: variant;
begin
  // task 1
  G := TGuid.NewGuid;

  // task 2
  B := G.ToByteArray;  
  V := B;  // needed only for your usage elsewhere in your code
  
  // task 3
  SetLength(S,32);
  BinToHex(B,PWideChar(s),16);
  S := '0x' + S;
  
end;

我忽略了错误检查等,因为我想专注于核心功能。根据需要添加。