25

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
Filburt
  • 17,626
  • 12
  • 64
  • 115
Maximus
  • 629
  • 2
  • 7
  • 15
  • 2
    Did you try inserting *zero-or-one whitespace* at the desired places? Don't be afraid, the modified regex will not explode if you fiddle around a little. – Filburt Jun 20 '17 at 14:53
  • FWIW, that doesn't validate an iban reliably. There's much more needed to do that. – baao Jun 11 '18 at 20:18
  • A good, extensive list that could be used is [this one from Apache Commons](https://commons.apache.org/proper/commons-validator/apidocs/src-html/org/apache/commons/validator/routines/IBANValidator.html#line.30). You can collect all the patterns that have been defined and adapt them to accept white spaces. For example: `DE\d{2}\s*\d{4}\s*\d{4}\s*\d{4}\s*\d{4}\s*\d{2}` will match your first example. – Hannon Queiroz Dec 21 '18 at 14:48
  • 1
    To validate IBAN with all possible whitespaces, just remove the whitespaces before validating, makes things quite a bit simpler. But actually you just cannot fully validate by regex, as the main key for validation is the checksum. – A.P. Nov 27 '19 at 07:16
  • 1
    So, to validate an iban, I'd do: 1) normalize: convert to uppercase and remove anything but `[A-Z0-9]` 2) match pattern `[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}` 3) validate checksum. In addition, you can check the (country specific) length, but that is not needed in most use cases unless you want to rule out that someone had made up an IBAN by recalculating the checksum. – A.P. Nov 27 '19 at 07:23
  • I just wanted to know if there could be an IBAN, with empty spaces or "-" between ... and after reading a lot of complicated, not working regex, i did it my self. I thought i share it: `/(?<![A-Z])[A-Z]{2}(\h|\-)?[0-9]{2}(\h|\-)?[A-Z0-9](?:(\h|\-)?[A-Z0-9]){10,29}(?![A-Z0-9])/` – cottton Jul 23 '21 at 20:03

4 Answers4

61

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
 'droid@i.ban' // 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))
});
LukStorms
  • 28,916
  • 5
  • 31
  • 45
  • Where do you see the 4-character bank code in your regexes? Examples are NL20INGB0001234567 and GB82WEST12345698765432. – hansmbakker Jun 11 '18 at 16:15
  • 1
    @wind-rider The regexes were written to fit the formats the question asked for. Where some of the digits are grouped by 4. But indeed, the BBAN part of an IBAN can have up to 30 alphanumeric characters that are country-specific. – LukStorms Jun 11 '18 at 18:24
  • 1
    @wind-rider For completeness sake I've included a regex for other IBAN's. – LukStorms Jun 11 '18 at 19:51
  • 1
    @wind-rider After checking wikipedia and testing it against layouts from different countries I had to change the regex a bit. And included a regex101 test for it. – LukStorms Jun 11 '18 at 20:19
  • nice, I upvoted your answer :) – hansmbakker Jun 11 '18 at 20:20
  • @LukStorms why the positive look-ahead in the "across the world" regex? your regex101 test still works without it – Victor Basso Jul 10 '18 at 08:53
  • 1
    @Vituel To assure that the BBAN part doesn't contain more than 30 letters or digits. See defenition for IBAN. If it were just to depend on the other groups then a BBAN with (5*6)+3 could still pass. Regex isn't only about what the pattern allows, but also what it doesn't allow. – LukStorms Jul 10 '18 at 09:03
  • first of all, thanks a lot for this. But according to Wikipedia only upper case letters are allowed – Naryoril Aug 29 '18 at 08:04
  • @Naryoril Good point. Hadn't spotted that yet on the Wikipedia page. It's been corrected. And if someone now still wants to allow lowercase letters also, they can just add the `i` flag to ignore case. – LukStorms Aug 29 '18 at 08:18
  • I noticed another issue, it correctly recognizes AB00111122223333444455556666777788 but not AB00 1111 2222 3333 4444 5555 6666 7777 88. There is probably an issue with the spaces and the maximum length. – Naryoril Aug 29 '18 at 12:01
  • @Naryoril Thanks for testing it that thoroughly! Indeed, the `{2,6}` had to be changed to `{2,7}`. It wasn't a problem for the one without spaces because it then matches groups per 5 digits. But for one with spaces, `6*4=24` had to be `7*4=28`. It has been updated. (Btw, this is an old answer of mine, and I was kinda surprised some people somehow find it in the long list of SO questions) – LukStorms Aug 29 '18 at 13:26
  • A "smellsLikeIBAN" without limiting it to start and end of line: `/(?<![A-Z])[A-Z]{2}(\h|\-)?[0-9]{2}(\h|\-)?[A-Z0-9](?:(\h|\-)?[A-Z0-9]){10,29}(?![A-Z0-9])/`. Successfully tested with @LukStorms last test https://regex101.com/r/dzDqmM/56 (turn on `g` (global)) – cottton Jul 23 '21 at 20:06
6

Here is a suggestion that may works for the patterns you provided:

[A-Z]{2}\d{2} ?\d{4} ?\d{4} ?\d{4} ?\d{4} ?[\d]{0,2}

Try it on regex101


Explanation

  • [A-Z]{2}\d{2} ? 2 capital letters followed by 2 digits (optional space)
  • \d{4} ? 4 digits, repeated 4 times (optional space)
  • [\d]{0,2} 0 to 2 digits
Mistalis
  • 17,793
  • 13
  • 73
  • 97
2

You can use a regex like this:

^[A-Z]{2}\d{2} (?:\d{4} ){3}\d{4}(?: \d\d?)?$

Working demo

This will match only those string formats

Federico Piazza
  • 30,085
  • 15
  • 87
  • 123
  • Thank you but this doesn't works if you delete spaces. What I meant I can't understand how to handle any whitespaces positions (including no whitespaces). – Maximus Jun 20 '17 at 14:57
  • @Maximus, regular expression helps you match patterns, if you need to match arbitrary characters, then you will have to use a whitespace check all the time, for instance: `[A-Z]\s*[A-Z]\s*\d\s*\d\s*.....etc`. Your question is not clear, you should update it and add more description with more sample as well. However, if my answer answers your question and you just want to make spaces optional, you need just to append `?` to each blank space – Federico Piazza Jun 20 '17 at 15:03
1

It's probably best to look up the specifications for a correct IBAN number. But if you want to have a regex similar to your existing one, but with spaces, you can use the following one:

^[a-zA-Z]{2}[0-9]{2}\s?[a-zA-Z0-9]{4}\s?[0-9]{4}\s?[0-9]{3}([a-zA-Z0-9]\s?[a-zA-Z0-9]{0,4}\s?[a-zA-Z0-9]{0,4}\s?[a-zA-Z0-9]{0,4}\s?[a-zA-Z0-9]{0,3})?$

Here is a live example: https://regex101.com/r/ZyIPLD/1

ssc-hrep3
  • 15,024
  • 7
  • 48
  • 87