IBAN Regex design

Maximus picture Maximus · Jun 20, 2017 · Viewed 76.2k times · Source

Help me please to design Regex that will match all IBANs with all possible whitespaces. Because I've found that one, but it does not work with whitespaces.

[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}

I need at least that formats:

DE89 3704 0044 0532 0130 00
AT61 1904 3002 3457 3201
FR14 2004 1010 0505 0001 3

Answer

LukStorms picture LukStorms · Jun 20, 2017

Just to find the example IBAN's from those countries in a text :
Start with 2 letters then 2 digits.
Then allow a space before every 4 digits, optionally ending with 1 or 2 digits:

\b[A-Z]{2}[0-9]{2}(?:[ ]?[0-9]{4}){4}(?!(?:[ ]?[0-9]){3})(?:[ ]?[0-9]{1,2})?\b    

regex101 test here

Note that if the intention is to validate a complete string, that the regex can be simplified.
Since the negative look-ahead (?!...) won't be needed then.
And the word boundaries \b can be replaced by the start ^ and end $ of the line.

^[A-Z]{2}[0-9]{2}(?:[ ]?[0-9]{4}){4}(?:[ ]?[0-9]{1,2})?$

Also, it can be simplified even more if having the 4 groups of 4 connected digits doesn't really matter.

^[A-Z]{2}(?:[ ]?[0-9]){18,20}$

Extra

If you need to match an IBAN number from accross the world?
Then the BBAN part of the IBAN is allowed to have up to 30 numbers or uppercase letters. Reference
And can be written with either spaces or dashes or nothing in between.
For example: CC12-XXXX-12XX-1234-5678-9012-3456-7890-123

So the regex pattern to match a complete string with a long IBAN becomes a bit longer.

^([A-Z]{2}[ \-]?[0-9]{2})(?=(?:[ \-]?[A-Z0-9]){9,30}$)((?:[ \-]?[A-Z0-9]{3,5}){2,7})([ \-]?[A-Z0-9]{1,3})?$

regex101 test here

Also note, that a pure regex solution can't do calculations.
So to actually validate an IBAN number then extra code is required.

Example Javascript Snippet:

function smellsLikeIban(str){
 return /^([A-Z]{2}[ \-]?[0-9]{2})(?=(?:[ \-]?[A-Z0-9]){9,30}$)((?:[ \-]?[A-Z0-9]{3,5}){2,7})([ \-]?[A-Z0-9]{1,3})?$/.test(str);
}

function validateIbanChecksum(iban) {       
  const ibanStripped = iban.replace(/[^A-Z0-9]+/gi,'') //keep numbers and letters only
                           .toUpperCase(); //calculation expects upper-case
  const m = ibanStripped.match(/^([A-Z]{2})([0-9]{2})([A-Z0-9]{9,30})$/);
  if(!m) return false;
  
  const numbericed = (m[3] + m[1] + m[2]).replace(/[A-Z]/g,function(ch){
                        //replace upper-case characters by numbers 10 to 35
                        return (ch.charCodeAt(0)-55); 
                    });
  //The resulting number would be to long for javascript to handle without loosing precision.
  //So the trick is to chop the string up in smaller parts.
  const mod97 = numbericed.match(/\d{1,7}/g)
                          .reduce(function(total, curr){ return Number(total + curr)%97},'');

  return (mod97 === 1);
};

var arr = [
 'DE89 3704 0044 0532 0130 00', // ok
 'AT61 1904 3002 3457 3201', // ok
 'FR14 2004 1010 0505 0001 3', // wrong checksum
 'GB82-WEST-1234-5698-7654-32', // ok
 'NL20INGB0001234567', // ok
 'XX00 1234 5678 9012 3456 7890 1234 5678 90', // only smells ok
 'YY00123456789012345678901234567890', // only smells ok
 'NL20-ING-B0-00-12-34-567', // stinks, but still a valid checksum
 'XX22YYY1234567890123', // wrong checksum again
 '[email protected]' // This Is Not The IBAN You Are Looking For
];
arr.forEach(function (str) {
  console.log('['+ str +'] Smells Like IBAN:    '+ smellsLikeIban(str));
  console.log('['+ str +'] Valid IBAN Checksum: '+ validateIbanChecksum(str))
});