我正在使用 Delphi 7 維護一個舊專案。我需要將一個長的十六進制字串轉換為十進制字串。我在 C# 中搜索并找到了示例代碼,但在 Delphi 中找不到。我只有兩個選擇:
- 在 Delphi 7 中實作或使用函式。
- 在 Delphi 2010 中實作或使用函式,然后將其匯出為 DLL。
我正在處理的十六進制字串的最大長度是 40 個字符,這里是一個示例:
'6F000080B4D4B3426C66A655010001000080B4'
我使用rapidtables進行轉換,這里是輸出
'2475382888117010136950089026926167642744062132'
我希望有人已經解決了這個問題并且可以提供幫助。也許有人給我一個演算法,我可以用它在 Delphi 中撰寫一個函式。
注意:
在 Delphi 7 中,Int64 的最大正值是$7FFFFFFFFFFFFFFF= 9223372036854775807,這個值與我需要的相去甚遠。
uj5u.com熱心網友回復:
為了解決這個問題,我們需要將其分為三個步驟:
- 逐位轉換十六進制數。這導致解決如何
- 乘以十進制數,這反過來又需要我們能夠
- 逐位添加十進制數。
作為其他功能所需的小幫手,讓我擁有這些:
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 個)比一次又一次地呼叫此函式更有效。
- 稍后進行乘法運算時,我們可以用 fe 6 乘以相同的被加數來懶惰地呼叫此函式一次,以模擬乘以 6。
$30是字符的 ASCII 碼'0'- 減去潛在字符'0'to'9'得到'0'值0to 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是的 ASCII 值'A'- 減去潛在字符'A'to'Z'得到'A'我們的值0to 25,然后我們只需添加10。
測驗
字串是隱含的。空格只是出于光學原因。
| 功能 | 引數 | 結果 | 證明 |
|---|---|---|---|
Summe() |
123 456 | 579 | 腦 |
Summe() |
123 456 9999 | 10578 | MS calc,大腦 |
Produkt() |
36*12 | 504 | MS calc,大腦 |
Produkt() |
8 6426578999 * 9731421999 | 8 4105351216 9179999001 | rapidtables.com |
ConvertToDecimal() |
6F0000 80B4D4B3 426C66A6 55010001 000080B4 | 247538 2888117010 1369500890 2692616764 2744062132 | rapidtables.com |
概括
- 這是一個很好的練習,可以將基本/現有技能(算術)轉化為程式代碼,因為需要對邏輯的理解比對編程/語言的理解更多,因此它對初學者(仍在為編程苦苦掙扎)和專家(主要專注于關于任務自動化)。
- 就性能而言,這肯定遠非最佳,但應該很容易理解/理解/遵循而不是難以閱讀。同樣,它應該很容易翻譯成其他語言。
- 額外的練習是還可以添加減法和除法。
- 一些微小的細節被省略,fe 數字總是被期望沒有前導零 - 結果可能也有不需要的前導零。空字串被解釋為
0,但 的結果0也不會減少為空字串。 - 它適用于任何 String 型別,因為無論如何我們只對 ASCII 進行操作 - 無需區分
AnsiString,UnicodeString等等WideString。但是,Char用作系結到該選擇的型別。但是功能Chr()和Ord()應該再次支持一切。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/534520.html
