为什么我的 Luhn 校验和在 Windows 上计算正确,但在 Android 上计算不正确?
Why is my Luhn checksum calculated correctly on Windows but not on Android?
我在 Internet 上得到了这个 Luhn
函数,当我为 Windows32 VCL 编译它时它是正确的。但是当我编译它以在 Android 应用程序中使用时,我得到一个 False
而不是 True
我怀疑这是因为它正在转换为 utf8
到字节期望 ascii
待转换。
那么我该怎么做才能在 Android 上正常工作?
function Luhn(Code: string): Boolean;
var
i, sum: integer;
temp: byte;
begin
{ calcula o algorítimo luhn, usado no iccid }
sum := 0;
for i:= length(Code) downto 1 do
begin // Run the characters backwards
temp := byte(Code[i])-48; // Convert from ASCII to byte
if (length(Code)-i) mod 2 = 0
then sum := sum + temp // Odd characters just add
else if temp < 5
then sum := sum + 2*temp // Even characters add double
else sum := sum + (2*temp)-9; // or sum the digits of the doubling
end;
Result := sum mod 10 = 0; // Return true if sum ends in a 0
if Result then
Toast(Code+#13+'True')
else
Toast(Code+#13+'False');
end;
问题是,默认情况下,移动编译器的字符串从零开始,但桌面编译器的字符串默认从零开始。你需要调整你的代码来解决这个问题。变化
Code[i]
到
Code[i-1]
我推荐使用
{$ZEROBASEDSTRINGS ON}
如果您希望在移动和桌面目标之间共享代码。详情在这里:http://docwiki.embarcadero.com/RADStudio/en/Zero-based_strings_(Delphi)
一些旁白:
- 这里根本没有UTF-8。
- 该代码非常脆弱,如果代码点超出 ASCII 范围,将会严重失败。谨慎地进行一些完整性检查。
- 我建议使用
ord()
从字符中获取序数值。
- 您可能会考虑使用内部
odd()
函数而不是显式 mod
测试。
所以,我个人会这样写代码:
function LuhnChecksumValid(const Value: string): Boolean;
var
C: Char;
Digit: Integer;
Sum: Integer;
OddChar: Boolean;
begin
if Value.Length=0 then
Exit(False);
Sum := 0;
OddChar := odd(Value.Length);
for C in Value do
begin
Digit := ord(C) - ord('0');
if not InRange(Digit, 0, 9) then
Exit(False);
if OddChar then
inc(Sum, Digit)
else if Digit < 5 then
inc(Sum, 2*Digit)
else
inc(Sum, 2*Digit - 9);
OddChar := not OddChar;
end;
Exit((9*Sum) mod 10 = 0);
end;
请注意,我有意避免使用索引。这样做可以让我们完全回避基于零或基于一的索引的问题。
正如 David 所说,这个问题与桌面和移动编译器在默认情况下以不同方式索引字符串有关。字符串在桌面编译器中仍为 1 索引,但在移动编译器中默认为 0 索引。这在文档中有描述:
Migrating Delphi Code to Mobile from Desktop | Use 0-Based Strings
要使代码以您不必处理 ZBS 差异的方式工作,您可以:
在移动设备上使用 {$ZEROBASEDSTRINGS OFF}
返回基于 1 的索引
{$IFDEF NEXTGEN}
{$ZEROBASEDSTRINGS OFF}
{$ENDIF}
function Luhn(Code: string): Boolean;
var
i, sum: integer;
temp: byte;
begin
{ calcula o algorítimo luhn, usado no iccid }
sum := 0;
for i := Length(Code) downto 1 do begin // Run the characters backwards
temp := byte(Ord(Code[i]))-48; // Convert from ASCII to byte
if (Length(Code)-i) mod 2 = 0 then
sum := sum + temp // Odd characters just add
else if temp < 5 then
sum := sum + (2*temp) // Even characters add double
else
sum := sum + (2*temp) - 9; // or sum the digits of the doubling
end;
Result := sum mod 10 = 0; // Return true if sum ends in a 0
if Result then
Toast(Code+#13+'True')
else
Toast(Code+#13+'False');
end;
{$IFDEF NEXTGEN}
{$ZEROBASEDSTRINGS ON}
{$ENDIF}
使用Low(String)
和High(String)
系统函数:
function Luhn(Code: string): Boolean;
var
i, sum: integer;
temp: byte;
begin
{ calcula o algorítimo luhn, usado no iccid }
sum := 0;
for i := High(Code) downto Low(Code) do begin // Run the characters backwards
temp := byte(Ord(Code[i]))-48; // Convert from ASCII to byte
if (High(Code)-i) mod 2 = 0 then
sum := sum + temp // Odd characters just add
else if temp < 5 then
sum := sum + (2*temp) // Even characters add double
else
sum := sum + (2*temp) - 9; // or sum the digits of the doubling
end;
Result := sum mod 10 = 0; // Return true if sum ends in a 0
if Result then
Toast(Code+#13+'True')
else
Toast(Code+#13+'False');
end;
使用 TStringHelper
助手 class:
uses
..., SysUtils;
function Luhn(Code: string): Boolean;
var
i, sum: integer;
temp: byte;
begin
{ calcula o algorítimo luhn, usado no iccid }
sum := 0;
for i := Code.Length-1 downto 0 do begin // Run the characters backwards
temp := byte(Ord(Code.Chars[i]))-48; // Convert from ASCII to byte
if (Code.Length-1-i) mod 2 = 0 then
sum := sum + temp // Odd characters just add
else if temp < 5 then
sum := sum + (2*temp) // Even characters add double
else
sum := sum + (2*temp) - 9; // or sum the digits of the doubling
end;
Result := sum mod 10 = 0; // Return true if sum ends in a 0
if Result then
Toast(Code+#13+'True')
else
Toast(Code+#13+'False');
end;
我在 Internet 上得到了这个 Luhn
函数,当我为 Windows32 VCL 编译它时它是正确的。但是当我编译它以在 Android 应用程序中使用时,我得到一个 False
而不是 True
我怀疑这是因为它正在转换为 utf8
到字节期望 ascii
待转换。
那么我该怎么做才能在 Android 上正常工作?
function Luhn(Code: string): Boolean;
var
i, sum: integer;
temp: byte;
begin
{ calcula o algorítimo luhn, usado no iccid }
sum := 0;
for i:= length(Code) downto 1 do
begin // Run the characters backwards
temp := byte(Code[i])-48; // Convert from ASCII to byte
if (length(Code)-i) mod 2 = 0
then sum := sum + temp // Odd characters just add
else if temp < 5
then sum := sum + 2*temp // Even characters add double
else sum := sum + (2*temp)-9; // or sum the digits of the doubling
end;
Result := sum mod 10 = 0; // Return true if sum ends in a 0
if Result then
Toast(Code+#13+'True')
else
Toast(Code+#13+'False');
end;
问题是,默认情况下,移动编译器的字符串从零开始,但桌面编译器的字符串默认从零开始。你需要调整你的代码来解决这个问题。变化
Code[i]
到
Code[i-1]
我推荐使用
{$ZEROBASEDSTRINGS ON}
如果您希望在移动和桌面目标之间共享代码。详情在这里:http://docwiki.embarcadero.com/RADStudio/en/Zero-based_strings_(Delphi)
一些旁白:
- 这里根本没有UTF-8。
- 该代码非常脆弱,如果代码点超出 ASCII 范围,将会严重失败。谨慎地进行一些完整性检查。
- 我建议使用
ord()
从字符中获取序数值。 - 您可能会考虑使用内部
odd()
函数而不是显式mod
测试。
所以,我个人会这样写代码:
function LuhnChecksumValid(const Value: string): Boolean;
var
C: Char;
Digit: Integer;
Sum: Integer;
OddChar: Boolean;
begin
if Value.Length=0 then
Exit(False);
Sum := 0;
OddChar := odd(Value.Length);
for C in Value do
begin
Digit := ord(C) - ord('0');
if not InRange(Digit, 0, 9) then
Exit(False);
if OddChar then
inc(Sum, Digit)
else if Digit < 5 then
inc(Sum, 2*Digit)
else
inc(Sum, 2*Digit - 9);
OddChar := not OddChar;
end;
Exit((9*Sum) mod 10 = 0);
end;
请注意,我有意避免使用索引。这样做可以让我们完全回避基于零或基于一的索引的问题。
正如 David 所说,这个问题与桌面和移动编译器在默认情况下以不同方式索引字符串有关。字符串在桌面编译器中仍为 1 索引,但在移动编译器中默认为 0 索引。这在文档中有描述:
Migrating Delphi Code to Mobile from Desktop | Use 0-Based Strings
要使代码以您不必处理 ZBS 差异的方式工作,您可以:
在移动设备上使用
{$ZEROBASEDSTRINGS OFF}
返回基于 1 的索引{$IFDEF NEXTGEN} {$ZEROBASEDSTRINGS OFF} {$ENDIF} function Luhn(Code: string): Boolean; var i, sum: integer; temp: byte; begin { calcula o algorítimo luhn, usado no iccid } sum := 0; for i := Length(Code) downto 1 do begin // Run the characters backwards temp := byte(Ord(Code[i]))-48; // Convert from ASCII to byte if (Length(Code)-i) mod 2 = 0 then sum := sum + temp // Odd characters just add else if temp < 5 then sum := sum + (2*temp) // Even characters add double else sum := sum + (2*temp) - 9; // or sum the digits of the doubling end; Result := sum mod 10 = 0; // Return true if sum ends in a 0 if Result then Toast(Code+#13+'True') else Toast(Code+#13+'False'); end; {$IFDEF NEXTGEN} {$ZEROBASEDSTRINGS ON} {$ENDIF}
使用
Low(String)
和High(String)
系统函数:function Luhn(Code: string): Boolean; var i, sum: integer; temp: byte; begin { calcula o algorítimo luhn, usado no iccid } sum := 0; for i := High(Code) downto Low(Code) do begin // Run the characters backwards temp := byte(Ord(Code[i]))-48; // Convert from ASCII to byte if (High(Code)-i) mod 2 = 0 then sum := sum + temp // Odd characters just add else if temp < 5 then sum := sum + (2*temp) // Even characters add double else sum := sum + (2*temp) - 9; // or sum the digits of the doubling end; Result := sum mod 10 = 0; // Return true if sum ends in a 0 if Result then Toast(Code+#13+'True') else Toast(Code+#13+'False'); end;
使用
TStringHelper
助手 class:uses ..., SysUtils; function Luhn(Code: string): Boolean; var i, sum: integer; temp: byte; begin { calcula o algorítimo luhn, usado no iccid } sum := 0; for i := Code.Length-1 downto 0 do begin // Run the characters backwards temp := byte(Ord(Code.Chars[i]))-48; // Convert from ASCII to byte if (Code.Length-1-i) mod 2 = 0 then sum := sum + temp // Odd characters just add else if temp < 5 then sum := sum + (2*temp) // Even characters add double else sum := sum + (2*temp) - 9; // or sum the digits of the doubling end; Result := sum mod 10 = 0; // Return true if sum ends in a 0 if Result then Toast(Code+#13+'True') else Toast(Code+#13+'False'); end;