Recent

Author Topic: Bad Wiki example of IsValidIBAN(s)  (Read 4726 times)

Alextp

  • Hero Member
  • *****
  • Posts: 1554
    • UVviewsoft
Bad Wiki example of IsValidIBAN(s)
« on: November 28, 2021, 07:55:24 pm »
https://wiki.freepascal.org/International_Bank_Account_Number

This code fails on several OK numbers from this table:
https://en.wikipedia.org/wiki/International_Bank_Account_Number#Structure

e.g. on Brazil code / CostaRica code.

Also it contains call of DelSpace which must be replaced to StringReplace().

winni

  • Hero Member
  • *****
  • Posts: 2850
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #1 on: November 28, 2021, 08:13:10 pm »
Hi

a) The example for the brasilian IBAN is wrong.
  Take the german issue of wikipedia where it is ok:
   https://de.wikipedia.org/wiki/Internationale_Bankkontonummer

b) It is international  standard to group the IBAN in blocks of 4 alphadecimals for better readability. Every child knows that the spaces exists only for readability.

Winni

Alextp

  • Hero Member
  • *****
  • Posts: 1554
    • UVviewsoft
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #2 on: November 28, 2021, 08:19:35 pm »
It fails not only on Brazil code.

DelSpaces() call is not LCL usual call, it must be replaced.

Bart

  • Hero Member
  • *****
  • Posts: 4604
    • Bart en Mariska's Webstek
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #3 on: November 28, 2021, 11:10:27 pm »
DelSpaces() call is not LCL usual call, it must be replaced.


DelSPace is part of StrUtils unit that comes with fpc.
Not really a reason to replace it with StringReplace, which b.t.w. is also not part of LCL.

The code in the example feels a bit clumsy with the repeated copy()/delete() sequences.

Bart

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1799
  • Former Delphi 1-7, 10.2 user
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #4 on: November 29, 2021, 06:56:21 am »
If there's no objections, I will replace the existing code with:

Code: Pascal  [Select][+][-]
  1. Program iban;
  2.  
  3. {$mode objFPC} {$H+}
  4.  
  5. Uses
  6.  StrUtils, SysUtils;
  7.  
  8. // Calculate Modulo 97-10
  9. function StrMOD97(s: String): integer;
  10. const
  11.   modu = 97;
  12. var
  13.   sx: String;
  14.   isx, ic, p: Integer;
  15. begin
  16.    p := Length(s);
  17.    while (p > 9) do
  18.      begin
  19.        sx := Copy(s, 1, 9);
  20.        Delete(s, 1, 9);
  21.        isx := StrToInt(sx);
  22.        ic := isx mod modu;
  23.        s := IntToStr(ic) + s;
  24.        p := Length(s);
  25.      end;
  26.    isx := StrToInt(s);
  27.    if isx >= modu
  28.      then ic := isx mod modu
  29.    else ic := isx;
  30.   result := ic;
  31. end;
  32.  
  33. // IBAN letter to number algorithm
  34. function ReplaceLetterWithNumber(ch: char):string;
  35. begin
  36.   result := IntToStr(ord(upCase(ch)) - ord('A') + 10);
  37. end;  
  38.  
  39. // Check IBAN validity
  40. function IsValidIBAN( s: string ): boolean;
  41. var
  42.   a_char: char;
  43.   s1: string = '';
  44.   i: integer = 0;
  45. begin
  46.   // Delete any spaces
  47.   s := DelSpace(s);
  48.  
  49.   // Move country code and checksum to end of string
  50.   s := s + copy(s, 1, 4);
  51.   delete(s, 1, 4);
  52.  
  53.   // Convert any letters to numbers
  54.   for i := 1 to Length(s)  do
  55.     if((s[i]) in ['A'..'Z']) then
  56.       begin
  57.         a_char := s[i];
  58.         s1 := s1 + ReplaceLetterWithNumber(a_char);
  59.       end
  60.     else
  61.       s1 := s1 + s[i];
  62.  
  63.   result := (StrMOD97(s1) = 1);
  64. end;
  65.  
  66. begin
  67.   writeLn('GB82 WEST 1234 5698 7654 32: ', isValidIBAN('GB82WEST12345698765432'));
  68.   writeLn('GB82 TEST 1234 5698 7654 32: ', isValidIBAN('GB82TEST12345698765432'));
  69.   writeLn('CH93 0076 2011 6238 5295 7: ', isValidIBAN('CH93 0076 2011 6238 5295 7'));
  70.   writeLn('IL62 0108 0000 0009 9999 999: ', isValidIBAN('IL62 0108 0000 0009 9999 999'));
  71.   writeLn('GR16 0110 1250 0000 0001 2300 695: ', isValidIBAN('GR16 0110 1250 0000 0001 2300 695'));
  72.   writeLn('US12 3456 7890 0987 6543 210: ', isValidIBAN('US12 3456 7890 0987 6543 210'));
  73.   writeLn('LC14 BOSL 1234 5678 9012 3456 7890 1234: ', isValidIBAN('LC14 BOSL 1234 5678 9012 3456 7890 1234'));
  74.   writeLn('BR15 0000 0000 0000 1093 2840 814 P2: ', isValidIBAN('BR15 0000 0000 0000 1093 2840 814 P2'));
  75. end.
« Last Edit: November 29, 2021, 07:05:44 am by trev »
Lazarus 2.3, FPC 3.3.1 macOS 12.1 x86_64 Xcode 13.2
Lazarus 2.3, FPC 3.3.1 macOS 12.1 aarch64 Xcode 13.2
Lazarus 2.3, FPC 3.2.2 FreeBSD 13.0 amd64 VM
Lazarus 2.3, FPC 3.2.2 FreeBSD 12.2 amd64 VM
Lazarus 2.1 r61574 FPC 3.0.4 Ubuntu 20.04 VM
Lazarus 2.0.10 FPC 3.2.0 Win10 amd64 VM

Alextp

  • Hero Member
  • *****
  • Posts: 1554
    • UVviewsoft
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #5 on: November 29, 2021, 09:03:50 am »
' isx := StrToInt(sx);'
This gives crash in non-digit chars, so some check is needed.

Can we have list of example codes in an 'array[0..n] of string'?

wp

  • Hero Member
  • *****
  • Posts: 9158
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #6 on: November 29, 2021, 11:11:27 am »
' isx := StrToInt(sx);'
This gives crash in non-digit chars, so some check is needed.
Alphanumeric characters should not be present any more when StrMod97 is called.

I'd add some more checks before beginning the calculation:
- The string must not be empty, or more precisely: it should at least have length 4 to be able to do the following checks.
- The first two characters must be upper-case alphanumeric. It should probably also be checked whether they match with one of the country codes given in the wikipedia article.
- The third and fourth characters must be numeric.

Moving country code and check sum to the end could be done without the copy/delete operations by starting the for loop with char #5, and adding another for loop for chars #1 to #4.

Code: Pascal  [Select][+][-]
  1. Program iban;
  2.  
  3. {$mode objFPC} {$H+}
  4.  
  5. Uses
  6.  StrUtils, SysUtils;
  7.  
  8. // Calculate Modulo 97-10
  9. function StrMOD97(s: String): integer;
  10. const
  11.   modu = 97;
  12. var
  13.   sx: String;
  14.   isx, ic, p: Integer;
  15. begin
  16.    p := Length(s);
  17.    while (p > 9) do
  18.      begin
  19.        sx := Copy(s, 1, 9);
  20.        Delete(s, 1, 9);
  21.        isx := StrToInt(sx);
  22.        ic := isx mod modu;
  23.        s := IntToStr(ic) + s;
  24.        p := Length(s);
  25.      end;
  26.    isx := StrToInt(s);
  27.    if isx >= modu
  28.      then ic := isx mod modu
  29.    else ic := isx;
  30.   result := ic;
  31. end;
  32.  
  33. function ProcessChar(ch: char):string;
  34. begin
  35.   if (ch in ['0'..'9']) then
  36.     Result := ch
  37.   else
  38.   // IBAN letter to number algorithm
  39.   if (ch in ['A'..'Z', 'a'..'z']) then
  40.     result := IntToStr(ord(upCase(ch)) - ord('A') + 10)
  41.   else
  42.     result := '';
  43. end;  
  44.  
  45. // Check IBAN validity
  46. function IsValidIBAN(s: string): boolean;
  47. var
  48.   s1: string = '';
  49.   ch: String;
  50.   i: Integer;
  51. begin
  52.   Result := false;
  53.  
  54.   // Delete any spaces
  55.   s := DelSpace(s);
  56.  
  57.   // Obvious tests
  58.   if Length(s) < 4 then
  59.     exit;
  60.   if not ((s[1] in ['A'..'Z']) and (s[2] in ['A'..'Z'])) then
  61.     exit;
  62.   if not ((s[3] in ['0'..'9']) and (s[4] in ['0'..'9'])) then
  63.     exit;
  64.  
  65.   // Process number block first
  66.   for i := 5 to Length(s) do
  67.   begin
  68.     ch := ProcessChar(s[i]);    // Convert letters to numbers, keep numbers
  69.     if ch = '' then
  70.       exit;
  71.     s1 := s1 + ch;
  72.   end;
  73.   // Move country code and check sum to the end of the test string
  74.   for i := 1 to 4 do
  75.     s1 := s1 + ProcessChar(s[i]);
  76.  
  77.   result := (StrMOD97(s1) = 1);
  78. end;
  79.  
  80. type
  81.   TTestRec = record
  82.     TestIBAN: String;
  83.     Expected: Boolean;
  84.   end;
  85.  
  86. const
  87.   // Expected results validated by https://www.ibancalculator.com/iban_validieren.html
  88.   Tests: array[0..10] of TTestRec = (
  89.     (TestIBAN:'GB82 WEST 1234 5698 7654 32';             Expected: true),
  90.     (TestIBAN:'GB82 TEST 1234 5698 7654 32';             Expected: false),
  91.     (TestIBAN:'GB82 WEST12345698765432';                 Expected: true),
  92.     (TestIBAN:'GB 82WEST12345698765432';                 Expected: true),
  93.     (TestIBAN:'GB82 WEST123 456 987 654 32';             Expected: true),
  94.     (TestIBAN:'CH93 0076 2011 6238 5295 7';              Expected: true),
  95.     (TestIBAN:'IL62 0108 0000 0009 9999 999';            Expected: true),
  96.     (TestIBAN:'GR16 0110 1250 0000 0001 2300 695';       Expected: true),
  97.     (TestIBAN:'US12 3456 7890 0987 6543 210';            Expected: false),
  98.     (TestIBAN:'LC14 BOSL 1234 5678 9012 3456 7890 1234'; Expected: true),
  99.     (TestIBAN:'BR15 0000 0000 0000 1093 2840 814 P2';    Expected: true )
  100.   );
  101.   W = 40;
  102. var
  103.   i: Integer;
  104.   s: String;
  105.   ok: Boolean;
  106. begin
  107.   for i := 0 to High(Tests) do
  108.   begin
  109.     s := Tests[i].TestIBAN;
  110.     ok := IsValidIBAN(s);
  111.     if ok = Tests[i].Expected then
  112.     begin
  113.       if ok then
  114.         WriteLn(s:W, ': Correct IBAN correctly detected')
  115.       else
  116.         WriteLn(s:W, ': Incorrect IBAN correctly detected.');
  117.     end else
  118.     if Tests[i].Expected then
  119.       WriteLn(s:W, ': ERROR (Correct IBAN not detected)')
  120.     else
  121.       WriteLn(s:W, ': ERROR (Incorrect IBAN not detected)');
  122.   end;
  123.  
  124.   ReadLn;
  125. end.
« Last Edit: November 29, 2021, 12:01:07 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

Alextp

  • Hero Member
  • *****
  • Posts: 1554
    • UVviewsoft
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #7 on: November 29, 2021, 04:49:28 pm »
Thanks, posted the code from @wp to the wiki.

Bart

  • Hero Member
  • *****
  • Posts: 4604
    • Bart en Mariska's Webstek
Re: Bad Wiki example of IsValidIBAN(s)
« Reply #8 on: November 29, 2021, 08:34:51 pm »
Thanks, posted the code from @wp to the wiki.
And now the code for StrMOD97 is duplicated on the wiki. Not good.

Bart

 

TinyPortal © 2005-2018