问题描述
在我的应用程序中,我使用 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;
我忽略了错误检查等,因为我想专注于核心功能。根据需要添加。