Delphi:将长的十六进制字符串转换为十进制字符串



我正在用Delphi 7维护一个旧项目。我需要将一个长的十六进制字符串转换为十进制字符串。我在C#中搜索并找到了示例代码,但在Delphi中没有。我只有两个选择:

  1. 在Delphi7中实现或使用一个函数
  2. 在Delphi2010中实现或使用一个函数,然后将其导出为DLL

我正在处理的十六进制字符串的最大长度是40个字符,下面是一个示例:


'6F000080B4D4B3426C66A655010001000080B4'

我使用rapidtables进行转换,这里是输出

'2475382888117010136950089026926167642744062132'

我希望有人以前解决过这个问题,并能提供帮助。也许有人给了我一个算法,我可以用它在Delphi中编写函数。

注意:
在Delphi 7 for Int64中,最大正值为$7FFFFFFFFFFFFFFF=9223372036854775807,而这个值远远不是我所需要的。

为了解决这个问题,我们需要将其分为三个步骤:

  1. 逐位转换十六进制数字。这就解决了如何
  2. 将十进制数相乘,这反过来又需要我们能够
  3. 将十进制数字逐位相加

作为其他函数中需要的小助手,让我拥有这些:

type
TArrayString= Array of String;  // In case it doesn't exist already
// Fill a text with leading zeroes up to the desired length
procedure MinLen( var s: String; iLen: Integer );
begin
while Length( s )< iLen do s:= '0'+ s;
end;

添加

我们在学校里都学会了书面加法:把所有数字写在一行中,然后把每行的数字相加,然后把这个和的额外数字加到被加数的下一行。这也可以很容易地完成:

// Addition of multiple long numbers
function Summe( aSummand: TArrayString ): String;
var
iLenMax, iA, iPos, iSum, iAdvance: Integer;
c: Char;
begin
result:= '0';
case Length( aSummand ) of
0: exit;  // Nothing to add at all
1: begin
result:= aSummand[Low( aSummand )];  // Sum equals the only summand
exit;
end;
end;
// Find the longest text, then make all texts as long as the longest,
// so we can simply access an existing character at that position.
iLenMax:= 0;
for iA:= Low( aSummand ) to High( aSummand ) do begin
if Length( aSummand[iA] )> iLenMax then iLenMax:= Length( aSummand[iA] );
end;
for iA:= Low( aSummand ) to High( aSummand ) do MinLen( aSummand[iA], iLenMax );
MinLen( result, iLenMax );
// All have the same length: process from the least significant digit
// (right) to the most significant digit (left).
for iPos:= iLenMax downto 1 do begin
// Manual addition: write all numbers in one row, then add single
// digits per row. Nobody will ever give this function so many
// summands that the sum of single digits will come near the Integer
// capacity.
iSum:= 0;
for iA:= Low( aSummand ) to High( aSummand ) do begin
Inc( iSum, Ord( aSummand[iA][iPos] )- $30 );  // Add digit from each number
end;
Inc( iSum, Ord( result[iPos] )- $30 );  // Also add result's digit from potential previous carry
// Turn that Integer sum into text again, digit by digit
iAdvance:= 0;  // Exceeding the current position if we need to carry
while iSum> 0 do begin
c:= Chr( (iSum mod 10)+ $30 );  // Only the rightmost digit
if iPos- iAdvance< 1 then begin  // Outside the String?
result:= c+ result;  // Prepend
end else begin
result[iPos- iAdvance]:= c;  // Set new digit in overall sum
end;
iSum:= iSum div 10;  // This digit has been process, go to next one
Inc( iAdvance );  // Not in the current position anymore, but processing potential carries
end;
end;
end;

由于以下原因,我没有将其限制为始终为2个被加数:

  • 处理未知数量的被加数几乎不需要额外的工作
  • 一次添加多个被加数(而不是总是2)比一次又一次地调用此函数更有效
  • 稍后使用乘法,我们可以用同一个被加数的f.e.6来懒惰地调用这个函数一次,以模拟乘以6

$30是字符'0'的ASCII码-用'0'的值减去潜在字符'0''9',得到值09

乘法

我们在学校里也都学会了书面乘法:对于一个因子的每一个数字,计算该乘积(只能是0到9的"简单"乘法),按数字位置将所有乘积写在一行中(可以选择后面加零),然后将所有乘积相加(参考书面加法)。这也可以很容易地完成,因为我们现在已经解决了添加:

// Multiplication of two long numbers
function Produkt( s1, s2: String ): String;
var
iPos, iStep, iA, iNine: Integer;
aSummand, aStep: TArrayString;
begin
// For each digit of one factor we will make a separate multiplication
SetLength( aSummand, Length( s1 ) );
// This time it doesn't matter how long both numbers are: just again go
// from least significant digit (right) to most significant digit (left).
for iPos:= Length( s1 ) downto 1 do begin
iA:= Length( s1 )- iPos;  // Array index per digit
// As per our position the sum must be shifted by 10: first (rightmost)
// digit needs no shift (0), second needs one (10), third needs two (100)...
MinLen( aSummand[iA], iA );
// Current digit
iStep:= Ord( s1[iPos] )- $30;
case iStep of
0: ;  // Multiplication by 0 always results in 0, an empty summand equals one of "0"
1: aSummand[iA]:= s2+ aSummand[iA];  // No multiplication needed, just prepend with factor
else
// Cheap multiplication: use addition with 2 to 9 times the same summand
SetLength( aStep, iStep );
for iNine:= 0 to iStep- 1 do aStep[iNine]:= s2;
aSummand[iA]:= Summe( aStep )+ aSummand[iA];  // Prepend sum, zeroes must be trailing
end;
end;
// Now just add all sums that we got per digit
result:= Summe( aSummand );
end;

它本可以更短,因为Summe()已经可以处理0和1的和数了——我真的不需要区别对待。正如前面所说:简单的乘法是通过简单的加法来完成的——性能效率不高,但很容易理解。

十六进制到十进制的转换

由于我们现在可以进行加法和乘法运算,我们还可以转换非十进制基数的每个数字,并将结果增加到总体结果:

// Convert base 2..36 long number into base 10 long number
function ConvertToDecimal( sFrom: String; sBase: String= '16' ): String;
var
sStep, sPos: String;
cFrom: Char;
a: TArrayString;
begin
SetLength( a, 2 );
a[0]:= '0';  // Overall sum = result
sPos:= '1';  // Current digit's power
while Length( sFrom )> 0 do begin  // Process least significant digit (right)
cFrom:= sFrom[Length( sFrom )];
case cFrom of  // For now at max base 36 is supported, which includes hexadecimal
'0'.. '9': sStep:= cFrom;  // Same as text
'A'.. 'Z': sStep:= IntToStr( Ord( cFrom )- $41+ 10 );  // Turn "B" into "11"
end;
a[1]:= Produkt( sPos, sStep );  // Multiply current digit's value by current position's power
a[0]:= Summe( a );  // Add both product and current overall result
sPos:= Produkt( sPos, sBase );  // Increase power to next digit position
Delete( sFrom, Length( sFrom ), 1 );  // Remove rightmost digit
end;
result:= a[0];
end;

它甚至不仅适用于十六进制(16进制)输入,也适用于其他输入。$41'A'的ASCII值-用'A'的值减去潜在字符'A''Z',得到025的值,然后我们只将10添加到该值。

测试

字符串是隐含的。空间只是出于光学原因。

证明79>td>MS calc,brainMS计算,大脑rapidtables.com>247538 2888117010 1369500890 2692616764 2744062132
函数参数结果
Summe()123+456大脑
Summe()123+456+99910578
Produkt()36*12504
Produkt()8 642657899*9731421999841053512169179999001
ConvertToDecimal()6F000080B4D4B3 426C6A6 55010001 000080B4rapidtables.com