Recent

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

AlexTP

  • Hero Member
  • *****
  • Posts: 2384
    • 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: 3197
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: 2384
    • 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: 5275
    • 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: 2020
  • 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 »

AlexTP

  • Hero Member
  • *****
  • Posts: 2384
    • 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: 11853
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 »

AlexTP

  • Hero Member
  • *****
  • Posts: 2384
    • 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: 5275
    • 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