为什么我的 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 差异的方式工作,您可以:

  1. 在移动设备上使用 {$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}
    
  2. 使用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;
    
  3. 使用 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;