我正在用Delphi 7维护一个旧项目。我需要将一个长的十六进制字符串转换为十进制字符串。我在C#中搜索并找到了示例代码,但在Delphi中没有。我只有两个选择:
- 在Delphi7中实现或使用一个函数
- 在Delphi2010中实现或使用一个函数,然后将其导出为DLL
我正在处理的十六进制字符串的最大长度是40个字符,下面是一个示例:
'6F000080B4D4B3426C66A655010001000080B4'
我使用rapidtables进行转换,这里是输出
'2475382888117010136950089026926167642744062132'
我希望有人以前解决过这个问题,并能提供帮助。也许有人给了我一个算法,我可以用它在Delphi中编写函数。
注意:
在Delphi 7 for Int64中,最大正值为$7FFFFFFFFFFFFFFF
=9223372036854775807
,而这个值远远不是我所需要的。
为了解决这个问题,我们需要将其分为三个步骤:
- 逐位转换十六进制数字。这就解决了如何
- 将十进制数相乘,这反过来又需要我们能够
- 将十进制数字逐位相加
作为其他函数中需要的小助手,让我拥有这些:
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'
,得到值0
到9
。
乘法
我们在学校里也都学会了书面乘法:对于一个因子的每一个数字,计算该乘积(只能是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'
,得到0
到25
的值,然后我们只将10
添加到该值。
测试
字符串是隐含的。空间只是出于光学原因。
函数 | 参数 | 结果 | 证明
---|---|---|
Summe() | 123+456 | 79>td>大脑 |
Summe() | 123+456+999 | 10578 | MS calc,brain
Produkt() | 36*12 | 504 | MS计算,大脑
Produkt() | 8 642657899*9731421999 | 841053512169179999001 | rapidtables.com
ConvertToDecimal() | 6F000080B4D4B3 426C6A6 55010001 000080B4 | >247538 2888117010 1369500890 2692616764 2744062132rapidtables.com |