IBAN

From Rosetta Code
Revision as of 19:58, 24 November 2013 by Ulrie (talk | contribs)
IBAN is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
This page uses content from Wikipedia. The original article was at IBAN. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)

The International Bank Account Number (IBAN) is an internationally agreed means of identifying bank accounts across national borders with a reduced risk of propagating transcription errors. The IBAN consists of up to 34 alphanumeric characters: first the two-letter ISO 3166-1 alpha-2 country code, then two check digits, and finally a country-specific Basic Bank Account Number (BBAN). The check digits enable a sanity check of the bank account number to confirm its integrity even before submitting a transaction.

The task here is to validate the following fictitious IBAN: GB82 WEST 1234 5698 7654 32. Details of the algorithm can be found on the Wikipedia page.

C

<lang C>#include <alloca.h>

  1. include <ctype.h>
  2. include <stdio.h>
  3. include <stdlib.h>
  4. include <string.h>
  1. define V(cc, exp) if (!strncmp(iban, cc, 2)) return len == exp

/* Validate country code against expected length. */ int valid_cc(const char *iban, int len) {

   V("AL", 28); V("AD", 24); V("AT", 20); V("AZ", 28); V("BE", 16); V("BH", 22); V("BA", 20); V("BR", 29);
   V("BG", 22); V("CR", 21); V("HR", 21); V("CY", 28); V("CZ", 24); V("DK", 18); V("DO", 28); V("EE", 20);
   V("FO", 18); V("FI", 18); V("FR", 27); V("GE", 22); V("DE", 22); V("GI", 23); V("GR", 27); V("GL", 18);
   V("GT", 28); V("HU", 28); V("IS", 26); V("IE", 22); V("IL", 23); V("IT", 27); V("KZ", 20); V("KW", 30);
   V("LV", 21); V("LB", 28); V("LI", 21); V("LT", 20); V("LU", 20); V("MK", 19); V("MT", 31); V("MR", 27);
   V("MU", 30); V("MC", 27); V("MD", 24); V("ME", 22); V("NL", 18); V("NO", 15); V("PK", 24); V("PS", 29);
   V("PL", 28); V("PT", 25); V("RO", 24); V("SM", 27); V("SA", 24); V("RS", 22); V("SK", 24); V("SI", 19);
   V("ES", 24); V("SE", 24); V("CH", 21); V("TN", 24); V("TR", 26); V("AE", 23); V("GB", 22); V("VG", 24);
   return 0;

}

/* Remove blanks from s in-place, return its new length. */ int strip(char *s) {

   int i = -1, m = 0;
   while(s[++i]) {
       s[i - m] = s[i];
       m += s[i] <= 32;
   }
   s[i - m] = 0;
   return i - m;

}

/* Calculate the mod 97 of an arbitrarily large number (as a string). */ int mod97(const char *s, int len) {

   int i, j, parts = len / 7;
   char rem[10] = "00";
   for (i = 1; i <= parts + (len % 7 != 0); ++i) {
       strncpy(rem + 2, s + (i - 1) * 7, 7);
       j = atoi(rem) % 97;
       rem[0] = j / 10 + '0';
       rem[1] = j % 10 + '0';
   }
   return atoi(rem) % 97;

}

int valid_iban(char *iban) {

   int i, j, l = 0, sz = strip(iban);
   char *rot, *trans;
   /* Ensure upper alphanumeric input and count letters. */
   for (i = 0; i < sz; ++i) {
       if (!isdigit(iban[i]) && !isupper(iban[i]))
           return 0;
       l += !!isupper(iban[i]);
   }
   if (!valid_cc(iban, sz))
       return 0;
   /* Move the first four characters to the end. */
   rot = alloca(sz);
   strcpy(rot, iban + 4);
   strncpy(rot + sz - 4, iban, 4);
   /* Allocate space for the transformed IBAN. */
   trans = alloca(sz + l);
   trans[sz + l] = 0;
   /* Convert A to 10, B to 11, etc. */
   for (i = j = 0; i < sz; ++i, ++j) {
       if (isdigit(rot[i]))
           trans[j] = rot[i];
       else {
           trans[j]   = (rot[i] - 55) / 10 + '0';
           trans[++j] = (rot[i] - 55) % 10 + '0';
       }
   }
   return mod97(trans, sz + l) == 1;

}

int main(int _, char **argv) {

   while (--_, *++argv)
       printf("%s is %svalid.\n", *argv, valid_iban(*argv) ? "" : "in");
   return 0;

}</lang>

Output:
iban 'GB82 WEST 1234 5698 7654 32' GB82TEST12345698765432
GB82WEST12345698765432 is valid.
GB82TEST12345698765432 is invalid.

C++

<lang C++>#include <string>

  1. include <iostream>
  2. include <boost/algorithm/string.hpp>
  3. include <map>
  4. include <algorithm>
  5. include <cctype>

using namespace boost::algorithm ;

bool isValid ( const std::string &ibanstring ) {

  static std::map<std::string, int> countrycodes 
                          { {"AL" , 28} , {"AD" , 24} , {"AT" , 20} , {"AZ" , 28 } ,

{"BE" , 16} , {"BH" , 22} , {"BA" , 20} , {"BR" , 29 } , {"BG" , 22} , {"CR" , 21} , {"HR" , 21} , {"CY" , 28 } , {"CZ" , 24} , {"DK" , 18} , {"DO" , 28} , {"EE" , 20 } , {"FO" , 18} , {"FI" , 18} , {"FR" , 27} , {"GE" , 22 } ,

                          {"DE" , 22} , {"GI" , 23} , {"GR" , 27} , {"GL" , 18 } ,
                          {"GT" , 28} , {"HU" , 28} , {"IS" , 26} , {"IE" , 22 } , 

{"IL" , 23} , {"IT" , 27} , {"KZ" , 20} , {"KW" , 30 } , {"LV" , 21} , {"LB" , 28} , {"LI" , 21} , {"LT" , 20 } , {"LU" , 20} , {"MK" , 19} , {"MT" , 31} , {"MR" , 27 } , {"MU" , 30} , {"MC" , 27} , {"MD" , 24} , {"ME" , 22 } , {"NL" , 18} , {"NO" , 15} , {"PK" , 24} , {"PS" , 29 } , {"PL" , 28} , {"PT" , 25} , {"RO" , 24} , {"SM" , 27 } , {"SA" , 24} , {"RS" , 22} , {"SK" , 24} , {"SI" , 19 } , {"ES" , 24} , {"SE" , 24} , {"CH" , 21} , {"TN" , 24 } , {"TR" , 26} , {"AE" , 23} , {"GB" , 22} , {"VG" , 24 } } ;

  std::string teststring( ibanstring ) ;
  erase_all( teststring , " " ) ; //defined in boost/algorithm/string.hpp
  if ( countrycodes.find( teststring.substr(0 , 2 )) == countrycodes.end( ) ) 
     return false ;
  if ( teststring.length( ) != countrycodes[ teststring.substr( 0 , 2 ) ] ) 
     return false ;
  if ( ! ( all ( teststring , is_alnum( ) ) ) ) 
     return false ;
  to_upper( teststring ) ;
  teststring = teststring.append( teststring.substr( 0 , 4 ) ) ;
  teststring.assign( teststring.substr( 4 ) ) ;
  std::string numberstring ;//will contain the letter substitutions
  for ( int i = 0 ; i < teststring.length( ) ; i++ ) {
     if ( std::isdigit( teststring[ i ] ) ) 

numberstring = numberstring + teststring[ i ]  ;

     if ( std::isupper( teststring[ i ] ) ) 

numberstring = numberstring + std::to_string( static_cast<int>( teststring[ i ] ) - 55 ) ;

  }
  //implements a stepwise check for mod 97 in chunks of 9 at the first time
  // , then in chunks of seven prepended by the last mod 97 operation converted
  //to a string
  int segstart = 0 ;
  int step = 9 ;
  std::string prepended ;
  long number = 0 ;
  while ( segstart  < numberstring.length( ) - step ) {
     number = std::stol( prepended + numberstring.substr( segstart , step ) ) ;
     int remainder = number % 97 ;
     prepended =  std::to_string( remainder ) ;
     if ( remainder < 10 ) 

prepended = "0" + prepended ;

     segstart = segstart + step ;
     step = 7 ;
  }
  number = std::stol( prepended + numberstring.substr( segstart )) ;
  return ( number % 97 == 1 ) ;

}

int main( ) {

  std::cout << "GB82 WEST 1234 5698 7654 32 is " << ( isValid( "GB82 WEST 1234 5698 7654 32" ) ? "" : "not " ) 
     << "valid!" << std::endl ;
  std::cout << "GB82TEST12345698765432 is " << ( isValid( "GB82TEST12345698765432" ) ? "" : "not " ) 

<< "valid!" << std::endl ;

  return 0 ;

}</lang>

Output:
GB82 WEST 1234 5698 7654 32 is valid!
GB82TEST12345698765432 is not valid!

Caché ObjectScript

<lang cos>Class Utils.Validate [ Abstract ] {

ClassMethod VerifyIBAN(pIBAN As %String = "") As %Boolean { // remove spaces and define parts Set iban=$Translate(pIBAN, " ") Set cc=$Extract(iban, 1, 2) Set cd=$Extract(iban, 3, 4) Set bban=$Extract(iban, 5, *)

// ensure IBAN is correct format If $Match(iban, ..GetIBANPattern(cc))=0 Quit 0

// compare result and return Quit cd=..GetIBANCheckDigit(cc, bban) }

ClassMethod GetIBANCheckDigit(pCC As %String, pBBAN As %String) As %Integer [ Internal, Private ] { Set str=pBBAN_pCC_"00" For i=1:1 { Set chr=$Extract(str, i) If chr="" Quit If chr?1U Set $Extract(str, i)=$ASCII(chr)-55 } Set cd=98-..GetModulus(str, 97) Quit $Select($Length(cd)=2: cd, 1: "0"_cd) }

ClassMethod GetModulus(pNum As %Integer, pDiv As %Integer) As %Integer [ Internal, Private ] { While $Length(pNum)>9 { Set $Extract(pNum, 1, 9)=$Extract(pNum, 1, 9)#pDiv } Quit pNum#pDiv }

ClassMethod GetIBANPattern(pCC As %String = "") As %String [ Internal, Private ] { Quit $Case(pCC, "AL": "^AL\d{10}[0-9A-Z]{16}$", "AD": "^AD\d{10}[0-9A-Z]{12}$", "AT": "^AT\d{18}$", "BH": "^BH\d{2}[A-Z]{4}[0-9A-Z]{14}$", "BE": "^BE\d{14}$", "BA": "^BA\d{18}$", "BG": "^BG\d{2}[A-Z]{4}\d{6}[0-9A-Z]{8}$", "HR": "^HR\d{19}$", "CY": "^CY\d{10}[0-9A-Z]{16}$", "CZ": "^CZ\d{22}$", "DK": "^DK\d{16}$|^FO\d{16}$|^GL\d{16}$", "DO": "^DO\d{2}[0-9A-Z]{4}\d{20}$", "EE": "^EE\d{18}$", "FI": "^FI\d{16}$", "FR": "^FR\d{12}[0-9A-Z]{11}\d{2}$", "GE": "^GE\d{2}[A-Z]{2}\d{16}$", "DE": "^DE\d{20}$", "GI": "^GI\d{2}[A-Z]{4}[0-9A-Z]{15}$", "GR": "^GR\d{9}[0-9A-Z]{16}$", "HU": "^HU\d{26}$", "IS": "^IS\d{24}$", "IE": "^IE\d{2}[A-Z]{4}\d{14}$", "IL": "^IL\d{21}$", "IT": "^IT\d{2}[A-Z]\d{10}[0-9A-Z]{12}$", "KZ": "^[A-Z]{2}\d{5}[0-9A-Z]{13}$", "KW": "^KW\d{2}[A-Z]{4}22!$", "LV": "^LV\d{2}[A-Z]{4}[0-9A-Z]{13}$", "LB": "^LB\d{6}[0-9A-Z]{20}$", "LI": "^LI\d{7}[0-9A-Z]{12}$", "LT": "^LT\d{18}$", "LU": "^LU\d{5}[0-9A-Z]{13}$", "MK": "^MK\d{5}[0-9A-Z]{10}\d{2}$", "MT": "^MT\d{2}[A-Z]{4}\d{5}[0-9A-Z]{18}$", "MR": "^MR13\d{23}$", "MU": "^MU\d{2}[A-Z]{4}\d{19}[A-Z]{3}$", "MC": "^MC\d{12}[0-9A-Z]{11}\d{2}$", "ME": "^ME\d{20}$", "NL": "^NL\d{2}[A-Z]{4}\d{10}$", "NO": "^NO\d{13}$", "PL": "^PL\d{10}[0-9A-Z]{,16}n$", "PT": "^PT\d{23}$", "RO": "^RO\d{2}[A-Z]{4}[0-9A-Z]{16}$", "SM": "^SM\d{2}[A-Z]\d{10}[0-9A-Z]{12}$", "SA": "^SA\d{4}[0-9A-Z]{18}$", "RS": "^RS\d{20}$", "SK": "^SK\d{22}$", "SI": "^SI\d{17}$", "ES": "^ES\d{22}$", "SE": "^SE\d{22}$", "CH": "^CH\d{7}[0-9A-Z]{12}$", "TN": "^TN59\d{20}$", "TR": "^TR\d{7}[0-9A-Z]{17}$", "AE": "^AE\d{21}$", "GB": "^GB\d{2}[A-Z]{4}\d{14}$", : " ") }

}</lang>

Examples:
USER>For  { Read iban Quit:iban=""  Write " => ", ##class(Utils.Validate).VerifyIBAN(iban), ! }
GB82 WEST 1234 5698 7654 32 => 1
GB82 TEST 1234 5698 7654 32 => 0
GR16 0110 1250 0000 0001 2300 695 => 1
GB29 NWBK 6016 1331 9268 19 => 1
SA03 8000 0000 6080 1016 7519 => 1
CH93 0076 2011 6238 5295 7 => 1
IL62 0108 0000 0009 9999 999 => 1

USER>

Clojure

<lang Clojure>(def explen

 {"AL" 28 "AD" 24 "AT" 20 "AZ" 28 "BE" 16 "BH" 22 "BA" 20 "BR" 29
  "BG" 22 "CR" 21 "HR" 21 "CY" 28 "CZ" 24 "DK" 18 "DO" 28 "EE" 20
  "FO" 18 "FI" 18 "FR" 27 "GE" 22 "DE" 22 "GI" 23 "GR" 27 "GL" 18
  "GT" 28 "HU" 28 "IS" 26 "IE" 22 "IL" 23 "IT" 27 "KZ" 20 "KW" 30
  "LV" 21 "LB" 28 "LI" 21 "LT" 20 "LU" 20 "MK" 19 "MT" 31 "MR" 27
  "MU" 30 "MC" 27 "MD" 24 "ME" 22 "NL" 18 "NO" 15 "PK" 24 "PS" 29
  "PL" 28 "PT" 25 "RO" 24 "SM" 27 "SA" 24 "RS" 22 "SK" 24 "SI" 19
  "ES" 24 "SE" 24 "CH" 21 "TN" 24 "TR" 26 "AE" 23 "GB" 22 "VG" 24})

(defn valid-iban? [iban]

 (let [iban (apply str (remove #{\space \tab} iban))]
   (cond
     ; Ensure upper alphanumeric input.
     (not (re-find #"^[\dA-Z]+$" iban)) false
     ; Validate country code against expected length.
     (not= (explen (subs iban 0 2)) (count iban)) false
     :else
     (let [rot   (flatten (apply conj (split-at 4 iban)))
           trans (map #(read-string (str "36r" %)) rot)]
       (= 1 (mod (bigint (apply str trans)) 97))))))

(prn (valid-iban? "GB82 WEST 1234 5698 7654 32")  ; true

    (valid-iban? "GB82 TEST 1234 5698 7654 32")) ; false</lang>

COBOL

Works with: OpenCOBOL

<lang cobol> IDENTIFICATION DIVISION.

      PROGRAM-ID. iban-main.
      DATA DIVISION.
      WORKING-STORAGE SECTION.
      01  iban                    PIC X(50).
      01  iban-flag               PIC X.
          88  is-valid            VALUE "Y", FALSE "N".
      PROCEDURE DIVISION.
      main-line.
          MOVE "GB82 WEST 1234 5698 7654 32" TO iban
          PERFORM display-validity
          MOVE "GB82 TEST 1234 5698 7654 32" TO iban
          PERFORM display-validity
          GOBACK
          .
      display-validity.
          CALL "validate-iban" USING CONTENT iban, REFERENCE iban-flag
          IF is-valid
              DISPLAY FUNCTION TRIM(iban) " is valid."
          ELSE
              DISPLAY FUNCTION TRIM(iban) " is not valid."
          END-IF
          .
      END PROGRAM iban-main.


      IDENTIFICATION DIVISION.
      PROGRAM-ID. validate-iban.
      DATA DIVISION.
      WORKING-STORAGE SECTION.
      01  country-lengths-area    VALUE "AD24AE23AL28AT20AZ28BA20BE16"
          & "BG22BH22BR29CH21CR21CY28CZ24DE22DK18DO28EE20ES24FI18FO18F"
          & "R27GB22GE22GI23GL18GR27GT28HR21HU28IE22IL23IS26IT27KW30KZ"
          & "20LB28LI21LT20LU20LV21MC27MD24ME22MK19MR27MT31MU30NL18NO1"
          & "5PK24PL28PS29PT25RO24RS22SA24SE24SI19SK24SM27TN24TR26VG24"
          .
          03  country-lengths     OCCURS 64 TIMES
                                  INDEXED BY country-lengths-idx.
              05  country-code    PIC XX.
              05  country-len     PIC 99.
      01  offset                  PIC 99.
      01  i                       PIC 99.
      01  len                     PIC 99.
      LINKAGE SECTION.
      01  iban                    PIC X(50).
      01  valid-flag              PIC X.
          88  is-valid            VALUE "Y", FALSE "N".
      PROCEDURE DIVISION USING iban, valid-flag.
          MOVE FUNCTION UPPER-CASE(iban) TO iban
          CALL "remove-spaces" USING iban
          *> Check if country-code and length are correct
          INSPECT iban TALLYING len FOR CHARACTERS BEFORE SPACE
          SET country-lengths-idx TO 1
          SEARCH country-lengths
              AT END
                  SET is-valid TO FALSE
                  GOBACK
              WHEN country-code (country-lengths-idx) = iban (1:2)
                  IF country-len (country-lengths-idx) NOT = len
                      SET is-valid TO FALSE
                      GOBACK
                  END-IF
          END-SEARCH
          CALL "create-iban-number" USING CONTENT len, REFERENCE iban
          *> Mod 97 number formed.
          IF FUNCTION MOD(iban, 97) = 1
              SET is-valid TO TRUE
          ELSE
              SET is-valid TO FALSE
          END-IF
          .
      IDENTIFICATION DIVISION.
      PROGRAM-ID. remove-spaces.
      DATA DIVISION.
      WORKING-STORAGE SECTION.
      01  i                       PIC 99.
      01  offset                  PIC 99.
      LINKAGE SECTION.
      01  str                     PIC X(50).
      PROCEDURE DIVISION USING str.
          INITIALIZE offset
          PERFORM VARYING i FROM 1 BY 1 UNTIL i > 50
              EVALUATE TRUE
                  WHEN str (i:1) = SPACE
                      ADD 1 TO offset
              
                  WHEN offset NOT = ZERO
                      MOVE str (i:1) TO str (i - offset:1)
              END-EVALUATE
          END-PERFORM
          MOVE SPACES TO str (50 - offset + 1:)
          .
      END PROGRAM remove-spaces.


      IDENTIFICATION DIVISION.
      PROGRAM-ID. create-iban-number.
      DATA DIVISION.
      WORKING-STORAGE SECTION.
      01  first-four              PIC X(4).
      01  iban-num                PIC X(50).
      01  digit-num               PIC 99 VALUE 1.       
      01  i                       PIC 99.
      01  letter-num              PIC 99.
      LINKAGE SECTION.
      01  len                     PIC 99.
      01  iban                    PIC X(50).
      PROCEDURE DIVISION USING len, iban.
          *> Move characters into final positions.
          MOVE iban (1:4) TO first-four
          MOVE iban (5:) TO iban
          MOVE first-four TO iban (len - 3:)
          *> Convert letters to numbers.
          INITIALIZE iban-num, digit-num ALL TO VALUE
          PERFORM VARYING i FROM 1 BY 1
                  UNTIL i > len OR iban (i:1) = SPACE
              IF iban (i:1) IS NUMERIC
                  MOVE iban (i:1) TO iban-num (digit-num:1)
                  ADD 1 TO digit-num
              ELSE
                  COMPUTE letter-num =
                      FUNCTION ORD(iban (i:1)) - FUNCTION ORD("A") + 10
                  MOVE letter-num TO iban-num (digit-num:2)
                  ADD 2 TO digit-num
              END-IF
          END-PERFORM
          MOVE iban-num TO iban
          .
          
      END PROGRAM create-iban-number.
      END PROGRAM validate-iban.</lang>
Output:
GB82 WEST 1234 5698 7654 32 is valid.
GB82 TEST 1234 5698 7654 32 is not valid.

D

Translation of: Python

<lang d>import std.stdio, std.string, std.regex, std.conv, std.bigint,

      std.algorithm, std.ascii;

immutable int[string] country2len; static this() {

   country2len = ["AL":28, "AD":24, "AT":20, "AZ":28, "BE":16,
   "BH":22, "BA":20, "BR":29, "BG":22, "CR":21, "HR":21, "CY":28,
   "CZ":24, "DK":18, "DO":28, "EE":20, "FO":18, "FI":18, "FR":27,
   "GE":22, "DE":22, "GI":23, "GR":27, "GL":18, "GT":28, "HU":28,
   "IS":26, "IE":22, "IL":23, "IT":27, "KZ":20, "KW":30, "LV":21,
   "LB":28, "LI":21, "LT":20, "LU":20, "MK":19, "MT":31, "MR":27,
   "MU":30, "MC":27, "MD":24, "ME":22, "NL":18, "NO":15, "PK":24,
   "PS":29, "PL":28, "PT":25, "RO":24, "SM":27, "SA":24, "RS":22,
   "SK":24, "SI":19, "ES":24, "SE":24, "CH":21, "TN":24, "TR":26,
   "AE":23, "GB":22, "VG":24];

}

bool validIBAN(string iban) {

   // Ensure upper alphanumeric input.
   iban = iban.removechars(whitespace);
   if (!iban.match(r"^[\dA-Z]+$"))
       return false;
   // Validate country code against expected length.
   if (iban.length != country2len[iban[0 .. 2]])
       return false;
   // Shift and convert. BASE 36: 0..9,A..Z -> 0..35.
   iban = iban[4 .. $] ~ iban[0 .. 4];
   return iban.map!(c => [c].to!int(36).text).join.BigInt % 97 == 1;

}

void main() {

   foreach (account; ["GB82 WEST 1234 5698 7654 32",
                      "GB82 TEST 1234 5698 7654 32"])
       writefln("%s validation is: %s", account, account.validIBAN);

}</lang>

Output:
GB82 WEST 1234 5698 7654 32 validation is: true
GB82 TEST 1234 5698 7654 32 validation is: false

Perl

<lang Perl>#!/usr/bin/perl use strict ; use warnings ; use Math::BigInt ;

my %countrycodelengths = ( "AL" => 28, "AD" => 24, "AT" => 20, "AZ" => 28,

                          "BE" =>  16, "BH" =>  22, "BA" =>  20, "BR" =>  29,
                          "BG" =>  22, "CR" =>  21, "HR" =>  21, "CY" =>  28,

"CZ" => 24, "DK" => 18, "DO" => 28, "EE" => 20, "FO" => 18, "FI" => 18, "FR" => 27, "GE" => 22, "DE" => 22, "GI" => 23, "GR" => 27, "GL" => 18, "GT" => 28, "HU" => 28, "IS" => 26, "IE" => 22, "IL" => 23, "IT" => 27, "KZ" => 20, "KW" => 30, "LV" => 21, "LB" => 28, "LI" => 21, "LT" => 20, "LU" => 20, "MK" => 19, "MT" => 31, "MR" => 27, "MU" => 30, "MC" => 27, "MD" => 24, "ME" => 22, "NL" => 18, "NO" => 15, "PK" => 24, "PS" => 29, "PL" => 28, "PT" => 25, "RO" => 24, "SM" => 27, "SA" => 24, "RS" => 22, "SK" => 24, "SI" => 19, "ES" => 24, "SE" => 24, "CH" => 21, "TN" => 24, "TR" => 26, "AE" => 23, "GB" => 22, "VG" => 24 ) ; sub validate_iban {

  my $ibanstring = shift ;
  $ibanstring =~ s/\s+//g ;
  return 0 unless $ibanstring =~ /[0-9a-zA-Z]+/ ;
  $ibanstring = uc $ibanstring ;
  return 0 if ( not exists $countrycodelengths{ substr( $ibanstring , 0 , 2 ) }  );
  return 0 if length ( $ibanstring ) != $countrycodelengths{ substr( $ibanstring , 0 , 2 ) } ;
  $ibanstring =~ s/(.{4})(.+)/$2$1/ ;
  $ibanstring =~ s/([A-Z])/ord( $1 ) - 55/eg ;
  my $number = Math::BigInt->new( $ibanstring ) ;
  if ( $number->bmod( 97 ) == 1 ) {
     return 1 ;
  }
  else {
     return 0 ;
  }

}

if ( validate_iban( "GB82 WEST 1234 5698 7654 32" ) ) {

  print "GB82 WEST 1234 5698 7654 32 is a valid IBAN number!\n" ;

} else {

  print "Sorry! GB82 WEST 1234 5698 7654 32 is not valid!\n" ;

} if ( validate_iban( "GB82TEST12345698765432" ) ) {

  print "GB82TEST12345698765432 is valid!\n" ;

}</lang>

Output:
GB82 WEST 1234 5698 7654 32 is a valid IBAN number!
GB82TEST12345698765432 is invalid!


Perl 6

<lang perl6>subset IBAN of Str where sub ($_ is copy) {

   s:g/\s//;
   return False if m/<-[ 0..9 A..Z a..z ]>/ or .chars != <
       AD 24 AE 23 AL 28 AT 20 AZ 28 BA 20 BE 16 BG 22 BH 22 BR 29 CH 21
       CR 21 CY 28 CZ 24 DE 22 DK 18 DO 28 EE 20 ES 24 FI 18 FO 18 FR 27
       GB 22 GE 22 GI 23 GL 18 GR 27 GT 28 HR 21 HU 28 IE 22 IL 23 IS 26
       IT 27 KW 30 KZ 20 LB 28 LI 21 LT 20 LU 20 LV 21 MC 27 MD 24 ME 22
       MK 19 MR 27 MT 31 MU 30 NL 18 NO 15 PK 24 PL 28 PS 29 PT 25 RO 24
       RS 22 SA 24 SE 24 SI 19 SK 24 SM 27 TN 24 TR 26 VG 24
   >.hash{.substr(0,2).uc};

   s/(.**4)(.+)/$1$0/;
   return .subst(:g, /\D/, { :36(~$_) }) % 97 == 1;

}

say "$_ is {$_ ~~ IBAN ?? 'valid' !! 'invalid' }" for 'GB82 WEST 1234 5698 7654 32', 'gb82 west 1234 5698 7654 32', 'GB82 TEST 1234 5698 7654 32';</lang>

Output:
GB82 WEST 1234 5698 7654 32 is valid.
gb82 west 1234 5698 7654 32 is valid.
GB82 TEST 1234 5698 7654 32 is invalid.

Python

Translation of: Ruby

<lang python>import re

_country2length = dict(

   AL=28, AD=24, AT=20, AZ=28, BE=16, BH=22, BA=20, BR=29,
   BG=22, CR=21, HR=21, CY=28, CZ=24, DK=18, DO=28, EE=20,
   FO=18, FI=18, FR=27, GE=22, DE=22, GI=23, GR=27, GL=18,
   GT=28, HU=28, IS=26, IE=22, IL=23, IT=27, KZ=20, KW=30,
   LV=21, LB=28, LI=21, LT=20, LU=20, MK=19, MT=31, MR=27,
   MU=30, MC=27, MD=24, ME=22, NL=18, NO=15, PK=24, PS=29,
   PL=28, PT=25, RO=24, SM=27, SA=24, RS=22, SK=24, SI=19,
   ES=24, SE=24, CH=21, TN=24, TR=26, AE=23, GB=22, VG=24 )

def valid_iban(iban):

   # Ensure upper alphanumeric input.
   iban = iban.replace(' ',).replace('\t',)
   if not re.match(r'^[\dA-Z]+$', iban): 
       return False
   # Validate country code against expected length.
   if len(iban) != _country2length[iban[:2]]:
       return False
   # Shift and convert.
   iban = iban[4:] + iban[:4]
   digits = int(.join(str(int(ch, 36)) for ch in iban)) #BASE 36: 0..9,A..Z -> 0..35
   return digits % 97 == 1

if __name__ == '__main__':

   for account in ["GB82 WEST 1234 5698 7654 32", "GB82 TEST 1234 5698 7654 32"]:
       print('%s validation is: %s' % (account, valid_iban(account)))</lang>
Output:
GB82 WEST 1234 5698 7654 32 validation is: True
GB82 TEST 1234 5698 7654 32 validation is: False

Racket

<lang racket>

  1. lang racket

(define lens

 '([AL 28] [AD 24] [AT 20] [AZ 28] [BH 22] [BE 16] [BA 20] [BR 29] [BG 22]
   [CR 21] [HR 21] [CY 28] [CZ 24] [DK 18] [DO 28] [EE 20] [FO 18] [FI 18]
   [FR 27] [GE 22] [DE 22] [GI 23] [GR 27] [GL 18] [GT 28] [HU 28] [IS 26]
   [IE 22] [IL 23] [IT 27] [KZ 20] [KW 30] [LV 21] [LB 28] [LI 21] [LT 20]
   [LU 20] [MK 19] [MT 31] [MR 27] [MU 30] [MC 27] [MD 24] [ME 22] [NL 18]
   [NO 15] [PK 24] [PS 29] [PL 28] [PT 25] [RO 24] [SM 27] [SA 24] [RS 22]
   [SK 24] [SI 19] [ES 24] [SE 24] [CH 21] [TN 24] [TR 26] [AE 23] [GB 22]
   [VG 24]))

(define (valid-iban? str)

 (define str* (regexp-replace* #px"\\s+" str ""))
 (define c (cond [(regexp-match #rx"^[A-Z][A-Z]" str*)
                  => (λ(x) (assq (string->symbol (car x)) lens))]
                 [else #f]))
 (define (letter c)
   (number->string (+ (char->integer (string-ref c 0)) -65 10)))
 (and c (= (cadr c) (string-length str*))
      (regexp-match? #rx"[A-Z0-9]" str*)
      (let* ([x (string-append (substring str* 4) (substring str* 0 4))]
             [x (string->number (regexp-replace* #rx"[A-Z]" x letter))])
        (= 1 (modulo x 97)))))

(valid-iban? "GB82 WEST 1234 5698 7654 32") ; => #t (valid-iban? "GB82 TEST 1234 5698 7654 32") ; => #f </lang>

REXX

These REXX programs can validate an IBAN specified on the command line or from an internal list.

basic checking

<lang rexx>/*REXX program validates an IBAN (International Bank Account Number). */ @.= @.1 = 'GB82 WEST 1234 5698 7654 32 ' @.2 = 'Gb82 West 1234 5698 7654 32 ' @.3 = 'GB82 TEST 1234 5698 7654 32 ' @.4 = 'GR16 0110 1250 0000 0001 2300 695 ' @.5 = 'GB29 NWBK 6016 1331 9268 19 ' @.6 = 'SA03 8000 0000 6080 1016 7519 ' @.7 = 'CH93 0076 2011 6238 5295 7 ' @.8 = 'IL62 0108 0000 0009 9999 999 ' @.9 = 'IL62-0108-0000-0009-9999-999 ' @.10= 'US12 3456 7890 0987 6543 210 ' @.11= 'GR16 0110 1250 0000 0001 2300 695X ' parse arg @.0 /*get optional first argument.*/

                do k=0+(arg()==0)  while @.k\==  /*either: 0 or 1 ──►n*/
                r = validateIBAN(@.k)
                if r==0  then say '  valid IBAN:'    @.k
                         else say 'invalid IBAN:'    @.k     "  "      r
                if k==0 then leave    /*if user specified IBAN, we done*/
                end   /*k*/

exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────VALIDATEIBAN subroutine─────────────*/ valIdateIBAN: procedure; arg x; numeric digits 200 /*allow big #s*/ x=space(x,0); L=length(x) /*elide blanks, determine length.*/ cc = 'AD 24 AE 23 AL 28 AT 20 AZ 28 BA 20 BE 16 BG 22 BH 22 BR 29 CH 21',

    'CR 21 CY 28 CZ 24 DE 22 DK 18 DO 28 EE 20 ES 24 FI 18 FO 18 FR 27',
    'GB 22 GE 22 GI 23 GL 18 GR 27 GT 28 HR 21 HU 28 IE 22 IL 23 IS 26',
    'IT 27 KW 30 KZ 20 LB 28 LI 21 LT 20 LU 20 LV 21 MC 27 MD 24 ME 22',
    'MK 19 MR 27 MT 31 MU 30 NL 18 NO 15 PK 24 PL 28 PS 29 PT 25 RO 24',
    'RS 22 SA 24 SE 24 SI 19 SK 24 SM 27 TN 24 TR 26 VG 24' /*country,L*/

@abc# = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' /*alphabet & decimal digs*/ cc_=left(x,2) /*extract the IBAN country code. */ c#=wordpos(cc_,cc) /*find the country code index. */ if c#==0 then return '***error!*** invalid country code:' cc_ if \datatype(x,'A') then return '***error!*** invalid character:',

                                              substr(x,verify(x,@abc#),1)

cL=word(cc,c#+1) /*get length of country's IBAN. */ if cL\==L then return '***error!*** invalid IBAN length:' L ' (should be' cL")" y=substr(x,5)left(x,4) /*put 4 in front ───► the back. */ z= /*translate characters──►digits. */

      do j=1  for L;      _=substr(y,j,1)
      if datatype(_,'U')  then z=z || pos(_,@abc#)+9
                          else z=z || _
      end   /*j*/

if z//97==1 then return 0 /*check to see if correct modulus*/

                 return '***error!*** invalid check digits.'</lang>

output when using the default input:

  valid IBAN: GB82 WEST 1234 5698 7654 32
  valid IBAN: Gb82 West 1234 5698 7654 32
invalid IBAN: GB82 TEST 1234 5698 7654 32            ***error!*** invalid check digits.
  valid IBAN: GR16 0110 1250 0000 0001 2300 695
  valid IBAN: GB29 NWBK 6016 1331 9268 19
  valid IBAN: SA03 8000 0000 6080 1016 7519
  valid IBAN: CH93 0076 2011 6238 5295 7
  valid IBAN: IL62 0108 0000 0009 9999 999
invalid IBAN: IL62-0108-0000-0009-9999-999           ***error!*** invalid character: -
invalid IBAN: US12 3456 7890 0987 6543 210           ***error!*** invalid country code: US
invalid IBAN: GR16 0110 1250 0000 0001 2300 695X     ***error!*** invalid IBAN length: 28  (should be 27)

more checking

This version of the REXX program has more error checking:

  • checks for two countries that may not be valid (as per their entry date into the IBAN system)
  • checks some countries to make sure their check digits match a specific value

<lang rexx>/*REXX program validates an IBAN (International Bank Account Number). */ @.= @.1 = 'GB82 WEST 1234 5698 7654 32 ' @.2 = 'Gb82 West 1234 5698 7654 32 ' @.3 = 'GB82 TEST 1234 5698 7654 32 ' @.4 = 'GR16 0110 1250 0000 0001 2300 695 ' @.5 = 'GB29 NWBK 6016 1331 9268 19 ' @.6 = 'SA03 8000 0000 6080 1016 7519 ' @.7 = 'CH93 0076 2011 6238 5295 7 ' @.8 = 'IL62 0108 0000 0009 9999 999 ' @.9 = 'IL62-0108-0000-0009-9999-999 ' @.10= 'US12 3456 7890 0987 6543 210 ' @.11= 'GR16 0110 1250 0000 0001 2300 695X ' @.12= 'GT11 2222 3333 4444 5555 6666 7777 ' @.13= 'MK11 2222 3333 4444 555 ' parse arg @.0 /*get optional first argument.*/

                do k=0+(arg()==0)  while @.k\==  /*either: 0 or 1 ──►n*/
                r = validateIBAN(@.k)
                if r==0  then say '  valid IBAN:'    @.k
                         else say 'invalid IBAN:'    @.k     "  "      r
                if k==0 then leave    /*if user specified IBAN, we done*/
                end   /*k*/

exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────VALIDATEIBAN subroutine─────────────*/ valIdateIBAN: procedure; arg x; numeric digits 200 /*allow big #s*/ x=space(x,0); L=length(x) /*elide blanks, determine length.*/ cc = 'AD 24 AE 23 AL 28 AT 20 AZ 28 BA 20 BE 16 BG 22 BH 22 BR 29 CH 21',

    'CR 21 CY 28 CZ 24 DE 22 DK 18 DO 28 EE 20 ES 24 FI 18 FO 18 FR 27',
    'GB 22 GE 22 GI 23 GL 18 GR 27 GT 28 HR 21 HU 28 IE 22 IL 23 IS 26',
    'IT 27 KW 30 KZ 20 LB 28 LI 21 LT 20 LU 20 LV 21 MC 27 MD 24 ME 22',
    'MK 19 MR 27 MT 31 MU 30 NL 18 NO 15 PK 24 PL 28 PS 29 PT 25 RO 24',
    'RS 22 SA 24 SE 24 SI 19 SK 24 SM 27 TN 24 TR 26 VG 24' /*country,L*/

@abc# = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' /*alphabet & decimal digs*/ cc_=left(x,2); kk=substr(x,3,2) /*get IBAN country code, checkDig*/ c#=wordpos(cc_,cc) /*find the country code index. */ if c#==0 then return '***error!*** invalid country code:' cc_ if \datatype(x,'A') then return '***error!*** invalid character:',

                                              substr(x,verify(x,@abc#),1)

cL=word(cc,c#+1) /*get length of country's IBAN. */ if cL\==L then return '***error!*** invalid IBAN length:' L ' (should be' cL")" if cc_=='BR' & date("S")<20130701 then return "***error!*** invalid IBAN country, Brazil isn't valid until 1-July-2013." if cc_=='GT' & date("S")<20140701 then return "***error!*** invalid IBAN country, Guatemala isn't valid until 1-July-2014." if cc_=='BA' & kk\==39 then return "***error!*** invalid check digits for Bosnia and Herzegovina:" kk if cc_=='MK' & kk\==07 then return "***error!*** invalid check digits for Macedonia:" kk if cc_=='ME' & kk\==25 then return "***error!*** invalid check digits for Montenegro:" kk if cc_=='PT' & kk\==50 then return "***error!*** invalid check digits for Portugal:" kk if cc_=='SI' & kk\==56 then return "***error!*** invalid check digits for Slovenia:" kk y=substr(x,5)left(x,4) /*put 4 in front ───► the back. */ z= /*translate characters──►digits. */

      do j=1  for L;      _=substr(y,j,1)
      if datatype(_,'U')  then z=z || pos(_,@abc#)+9
                          else z=z || _
      end   /*j*/

if z//97==1 then return 0 /*check to see if correct modulus*/

                 return '***error!*** invalid check digits.'</lang>

output when using the default input,   (the run date of this program is   29-April-2013):

  valid IBAN: GB82 WEST 1234 5698 7654 32
  valid IBAN: Gb82 West 1234 5698 7654 32
invalid IBAN: GB82 TEST 1234 5698 7654 32            ***error!*** invalid check digits.
  valid IBAN: GR16 0110 1250 0000 0001 2300 695
  valid IBAN: GB29 NWBK 6016 1331 9268 19
  valid IBAN: SA03 8000 0000 6080 1016 7519
  valid IBAN: CH93 0076 2011 6238 5295 7
  valid IBAN: IL62 0108 0000 0009 9999 999
invalid IBAN: IL62-0108-0000-0009-9999-999           ***error!*** invalid character: -
invalid IBAN: US12 3456 7890 0987 6543 210           ***error!*** invalid country code: US
invalid IBAN: GR16 0110 1250 0000 0001 2300 695X     ***error!*** invalid IBAN length: 28  (should be 27)
invalid IBAN: GT11 2222 3333 4444 5555 6666 7777     ***error!*** invalid IBAN country, Guatemala isn't valid until 1-July-2014.
invalid IBAN: MK11 2222 3333 4444 555                ***error!*** invalid check digits for Macedonia: 11

Ruby

Works with: Ruby version 1.9+

<lang Ruby>def valid_iban? iban

 len = {
   AL: 28, AD: 24, AT: 20, AZ: 28, BE: 16, BH: 22, BA: 20, BR: 29,
   BG: 22, CR: 21, HR: 21, CY: 28, CZ: 24, DK: 18, DO: 28, EE: 20,
   FO: 18, FI: 18, FR: 27, GE: 22, DE: 22, GI: 23, GR: 27, GL: 18,
   GT: 28, HU: 28, IS: 26, IE: 22, IL: 23, IT: 27, KZ: 20, KW: 30,
   LV: 21, LB: 28, LI: 21, LT: 20, LU: 20, MK: 19, MT: 31, MR: 27,
   MU: 30, MC: 27, MD: 24, ME: 22, NL: 18, NO: 15, PK: 24, PS: 29,
   PL: 28, PT: 25, RO: 24, SM: 27, SA: 24, RS: 22, SK: 24, SI: 19,
   ES: 24, SE: 24, CH: 21, TN: 24, TR: 26, AE: 23, GB: 22, VG: 24
 }
 # Ensure upper alphanumeric input.
 iban.delete! " \t"
 return false unless iban =~ /^[\dA-Z]+$/
 # Validate country code against expected length.
 cc = iban[0, 2].to_sym
 return false unless iban.size == len[cc]
 # Shift and convert.
 iban = iban[4..-1] + iban[0, 4]
 iban.gsub!(/./) { |c| c.to_i(36) }
 iban.to_i % 97 == 1

end

p valid_iban? "GB82 WEST 1234 5698 7654 32" #=> true p valid_iban? "GB82 TEST 1234 5698 7654 32" #=> false</lang>

Tcl

<lang tcl>proc verifyIBAN {iban} {

   # Normalize by up-casing and stripping illegal chars (e.g., space)
   set iban [regsub -all {[^A-Z0-9]+} [string toupper $iban] ""]
   # Get the expected length from the country-code part
   switch [string range $iban 0 1] {

NO { set len 15 } BE { set len 16 } DK - FI - FO - GL - NL { set len 18} MK - SI { set len 19 } AT - BA - EE - KZ - LT - LU { set len 20 } CH - CR - HR - LI - LV { set len 21 } BG - BH - DE - GB - GE - IE - ME - RS { set len 22 } AE - GI - IL { set len 23 } AD - CZ - ES - MD - PK - RO - SA - SE - SK - TN - VG { set len 24 } PT { set len 25 } IS - TR { set len 26 } FR - GR - IT - MC - MR - SM { set len 27 } AL - AZ - CY - DO - GT - HU - LB - PL { set len 28 } BR - PS { set len 29 } KW - MU { set len 30 } MT { set len 31 } default { # unsupported country code return false }

   }
   # Convert to number
   set num [string map {

A 10 B 11 C 12 D 13 E 14 F 15 G 16 H 17 I 18 J 19 K 20 L 21 M 22 N 23 O 24 P 25 Q 26 R 27 S 28 T 29 U 30 V 31 W 32 X 33 Y 34 Z 35

   } [string range $iban 4 end][string range $iban 0 3]]
   # Verify length and modulus
   return [expr {[string length $iban] == $len && $num % 97 == 1}]

}</lang> Demonstrating: <lang tcl>set iban "GB82 WEST 1234 5698 7654 32" puts "$iban is [expr {[verifyIBAN $iban] ? {verified} : {unverified}}]" set not "GB42 WEST 1234 5698 7654 32" puts "$not is [expr {[verifyIBAN $not] ? {verified} : {unverified}}]"</lang>

Output:
GB82 WEST 1234 5698 7654 32 is verified
GB42 WEST 1234 5698 7654 32 is unverified

C#

<lang c#>

   public class IbanValidator : IValidateTypes
   {
       public ValidationResult Validate(string value)
       {
           // Check if value is missing
           if (string.IsNullOrEmpty(value))
               return ValidationResult.ValueMissing;
           if (value.Length < 2)
               return ValidationResult.ValueTooSmall;
           var countryCode = value.Substring(0, 2).ToUpper();
           var lengthForCountryCode = _lengths[countryCode];
           // Check length.
           if (value.Length < lengthForCountryCode)
               return ValidationResult.ValueTooSmall;
           if (value.Length > lengthForCountryCode)
               return ValidationResult.ValueTooBig;
           value = value.ToUpper();
           var newIban = value.Substring(4) + value.Substring(0, 4);
           newIban = Regex.Replace(newIban, @"\D", match => ((int) match.Value[0] - 55).ToString());
           var remainder = BigInteger.Parse(newIban) % 97;
           if (remainder != 1)
               return ValidationResult.ValueFailsModule97Check;


           return ValidationResult.IsValid;
       }
       private static Dictionary<string, int> _lengths = new Dictionary<string, int>
       {
           {"AL", 28},
           {"AD", 24},
           {"AT", 20},
           {"AZ", 28},
           {"BE", 16},
           {"BH", 22},
           {"BA", 20},
           {"BR", 29},
           {"BG", 22},
           {"CR", 21},
           {"HR", 21},
           {"CY", 28},
           {"CZ", 24},
           {"DK", 18},
           {"DO", 28},
           {"EE", 20},
           {"FO", 18},
           {"FI", 18},
           {"FR", 27},
           {"GE", 22},
           {"DE", 22},
           {"GI", 23},
           {"GR", 27},
           {"GL", 18},
           {"GT", 28},
           {"HU", 28},
           {"IS", 26},
           {"IE", 22},
           {"IL", 23},
           {"IT", 27},
           {"KZ", 20},
           {"KW", 30},
           {"LV", 21},
           {"LB", 28},
           {"LI", 21},
           {"LT", 20},
           {"LU", 20},
           {"MK", 19},
           {"MT", 31},
           {"MR", 27},
           {"MU", 30},
           {"MC", 27},
           {"MD", 24},
           {"ME", 22},
           {"NL", 18},
           {"NO", 15},
           {"PK", 24},
           {"PS", 29},
           {"PL", 28},
           {"PT", 25},
           {"RO", 24},
           {"SM", 27},
           {"SA", 24},
           {"RS", 22},
           {"SK", 24},
           {"SI", 19},
           {"ES", 24},
           {"SE", 24},
           {"CH", 21},
           {"TN", 24},
           {"TR", 26},
           {"AE", 23},
           {"GB", 22},
           {"VG", 24}
       };
   }

</lang>

Demonstrating:

<lang C#>

   public class When_the_IbanValidator_is_told_to_Validate
   {
       [Fact]
       public void It_should_return_an_error_when_there_is_no_value_provided()
       {
           // Assert
           const string value = "";
           var validator = new IbanValidator();
           // Act
           var result = validator.Validate(value);
           // Assert
           Assert.Equal(ValidationResult.ValueMissing, result);
       }
       [Fact]
       public void It_should_return_an_error_when_the_value_length_is_to_short()
       {
           // Assert
           const string value = "BE1800165492356";
           var validator = new IbanValidator();
           // Act
           var result = validator.Validate(value);
           // Assert
           Assert.Equal(ValidationResult.ValueTooSmall, result);
       }
       [Fact]
       public void It_should_return_an_error_when_the_value_length_is_to_big()
       {
           // Assert
           const string value = "BE180016549235656";
           var validator = new IbanValidator();
           // Act
           var result = validator.Validate(value);
           // Assert
           Assert.Equal(ValidationResult.ValueTooBig, result);
       }
       [Fact]
       public void It_should_return_an_error_when_the_value_fails_the_module_check()
       {
           // Assert
           const string value = "BE18001654923566";
           var validator = new IbanValidator();
           // Act
           var result = validator.Validate(value);
           // Assert
           Assert.Equal(ValidationResult.ValueFailsModule97Check, result);
       }
       [Fact]
       public void It_should_return_valid_when_a_valid_value_is_provided()
       {
           // Assert
           const string value = "BE18001654923565";
           var validator = new IbanValidator();
           // Act
           var result = validator.Validate(value);
           // Assert
           Assert.Equal(ValidationResult.IsValid, result);
       }
       [Fact]
       public void It_should_return_valid_when_a_valid_foreign_value_is_provided()
       {
           // Assert
           const string value = "GB82WEST12345698765432";
           var validator = new IbanValidator();
           // Act
           var result = validator.Validate(value);
           // Assert
           Assert.Equal(ValidationResult.IsValid, result);
       }
   }

</lang>