对于注册码,我想将Int64转换为base30(30只使用大写字符,不包括0,O,I,1等)并返回。
使用以下功能并不太难:
const
Base = 30;
Base30CharSet = '23456789ABCDEFGHJKLMNPRSTVWXYZ';
function ConvertIntToBase30(ANumber: Int64): string;
begin
if(ANumber = 0) then
Result := Copy(Base30CharSet, 1, 1)
else begin
Result := '';
while(ANumber <> 0) do begin
Result := Copy(Base30CharSet, (ANumber mod Base)+1, 1) + Result;
ANumber := ANumber div Base;
end;
end;
end;
function ConvertBase30ToInt(ANumber: string): Int64;
var
i: integer;
begin
Result := 0;
for i := 1 to Length(ANumber) do begin
Result := Result + (Pos(ANumber[i], Base30CharSet)-1);
if(i < Length(ANumber)) then
Result := Result * Base;
end;
end;
问题在于我对Int64的位感兴趣,所以我可以处理像$FFFFFFFFFFFFFFFF = -1
这样的数字。
为了解决这个问题,我想我会存储并删除符号(abs())并将符号作为附加到base30结果的额外字符包含在内。 Int64下限发生的问题是调用abs(-9223372036854775808)导致溢出。
有没有人有解决方案或更好的算法来解决这个问题?
答案 0 :(得分:1)
处理它的方法是让一个字符表示它是一个负数,这样你就可以解码了。对于负数,只需将位从1翻转为0并在编码前删除符号位,在解码时,执行回翻并添加符号位。以下是工作代码
function InvertIntOff(const ANumberL, ANumberH: Integer): Int64;
asm
XOR EAX,$FFFFFFFF
XOR EDX,$FFFFFFFF
end;
function InvertIntOn(const ANumberL, ANumberH: Integer): Int64;
asm
XOR EAX,$FFFFFFFF
XOR EDX,$FFFFFFFF
OR EDX,$80000000
end;
function ConvertIntToBase(ANumber: Int64): string;
const
CBaseMap: array[0..31] of Char = (
'2','3','4','5','6','7','8','9', //0-7
'A','B','C','D','E','F','G','H', //8-15
'J','K','L','M','N', //16-20
'P','Q','R','S','T','U','V','X','W','Y','Z'); //21-31
var
I: Integer;
begin
SetLength(Result, 15);
I := 0;
if ANumber < 0 then
begin
Inc(I);
Result[I] := '1';
ANumber := InvertIntOff(ANumber and $FFFFFFFF, (ANumber and $FFFFFFFF00000000) shr 32);
end;
while ANumber <> 0 do
begin
Inc(I);
Result[I] := CBaseMap[ANumber and $1F];
ANumber := ANumber shr 5;
end;
SetLength(Result, I);
end;
function ConvertBaseToInt(const ABase: string): Int64;
var
I, Index: Integer;
N: Int64;
begin
Result := 0;
if Length(ABase) > 0 then
begin
if ABase[1] = '1' then
Index := 2
else
Index := 1;
for I := Index to Length(ABase) do
begin
case ABase[I] of
'2'..'9':
N := Ord(ABase[I]) - Ord('2');
'A'..'H':
N := Ord(ABase[I]) - Ord('A') + 8;
'J'..'N':
N := Ord(ABase[I]) - Ord('J') + 16;
'P'..'Z':
N := Ord(ABase[I]) - Ord('P') + 21;
else
raise Exception.Create('error');
end;
if I > Index then
Result := Result or (N shl ((I - Index) * 5))
else
Result := N;
end;
if ABase[1] = '1' then
Result := InvertIntOn(Result and $FFFFFFFF, (Result and $FFFFFFFF00000000) shr 32);
end;
end;
procedure TestBase32;
var
S: string;
begin
S := ConvertIntToBase(-1);
ShowMessage(S + ' / ' + IntToStr(ConvertBaseToInt(S)) + ' ? -1');
S := ConvertIntToBase(-31);
ShowMessage(S + ' / ' + IntToStr(ConvertBaseToInt(S)) + ' ? -31');
S := ConvertIntToBase(1);
ShowMessage(S + ' / ' + IntToStr(ConvertBaseToInt(S)) + ' ? 1');
S := ConvertIntToBase(123456789);
ShowMessage(S + ' / ' + IntToStr(ConvertBaseToInt(S)) + ' ? 123456789');
S := ConvertIntToBase(-123456789);
ShowMessage(S + ' / ' + IntToStr(ConvertBaseToInt(S)) + ' ? -123456789');
end;
答案 1 :(得分:0)
我认为你几乎在考虑abs()......
但是为什么不简单地忽略用于处理Int64本身值的符号,而不是使用abs()?据我所知,你实际上已经这样做了,所以编码例程只需要一个小的补充:
if aNumber < 0 then
// negative
else
// positive;
唯一的问题是生成的Base30字符串中的符号信息丢失。因此,使用从 aNumber&lt;中获得的新信息将其视为要解决的单独问题。 0 测试...
我发现你排除了所有可能混淆为0或1的字符,但也排除了0和1。因此,您可以使用0和1表示正面或负面(反之亦然)。
根据这些例程的目的,在结果中放置0/1可能完全是任意的(如果你希望混淆事物并使0/1随机放置而不是一致的前导/跟踪字符)。
当编码时,只需将符号指示符随机地放入结果字符串中,并在解码时处理0/1字符,只要遇到符号标记,但为了解码该值而跳过。
当然,如果混淆不是问题,那么只需要一致地预先或后固定标志指示。
你甚至可以简单地选择使用'1'来表示负数,并使用'1'的LACK来表示/假设为正(这会简化我认为的零值情况)
答案 2 :(得分:0)
简单的答案是关闭范围检查,即使只是你正在调用abs的方法。
如果你不关心额外的一两个字符,你可以将int64分成单词或双字并将它们串在一起。我更想尝试使用base32并使用位移来提高速度和易用性。然后您的编码变为
Base32CharSet [(ANumber shr 5)%32]
和类似的基于pos()的解码方法。