Masking IBAN codes in C#

274 Views Asked by At

I have some IBAN code. For example it is Germany IBAN: DE12500123456789012345. I need to mask it and get result like this: DE*****12345*******345. But since the length of IBAN is different for each country I have following rule for masking: {2 letters}{5 asterisks}{5 numbers}{n asterisks}{3 numbere}. So in case DE IBAN - DE*****12345*******345, in case BE BE*****12345*345.

How to implement it with Regex for C#?

2

There are 2 best solutions below

3
Eldar On BEST ANSWER

Well not using Regex will be more easier and more performant in your case :

var iban = "DE12500123456789012345";
var maskedIban = $"{iban[0..2]}{"".PadRight(5, '*')}{iban[7..12]}{"".PadRight(iban.Length -15, '*')}{iban[^3..]}";
//  ^ "DE*****12345*******345"

Fiddle

Edit A regex solution which not as clean as the above, It uses named groups and a modified extension method that replaces named groups that is taken from this SO answer

public static class X
{
    public static string ReplaceGroup(this Regex regex, string input, string groupName, char pattern)
    {
        return regex.Replace(input, m =>
        {
            var group = m.Groups[groupName];
            var sb = new StringBuilder();
            var previousCaptureEnd = 0;
            foreach (var capture in group.Captures.Cast<Capture>())
            {
                var currentCaptureEnd = capture.Index + capture.Length - m.Index;
                var currentCaptureLength = capture.Index - m.Index - previousCaptureEnd;
                sb.Append(m.Value.Substring(previousCaptureEnd, currentCaptureLength));
                sb.Append("".PadRight(currentCaptureLength,pattern));
                previousCaptureEnd = currentCaptureEnd;
            }

            sb.Append(m.Value.Substring(previousCaptureEnd));
            return sb.ToString();
        });
    }
}

// Usage
var input = "DE12500123456789012345";
var groupPattern = @"^([A-Z]{2})(?<Ast>\d{5})(\d{5})(?<Ast>.*)(\d{3})$";
var groupRegex = new Regex(groupPattern);
var x = groupRegex.ReplaceGroup(input,"Ast", '*');

Fiddle

Edit Added @Wiktor Stribiżew solution to the benchmark:

Regex.Replace(text, @"(?<=^[A-Z]{2}(?:\d{0,4}|\d{10,}(?=\d{4,}$)))\d", "*")

Performance comparison of the three methods:

Method Iban Mean Error StdDev Gen0 Allocated
RegexConcat DE125(...)12345 [22] 7,492.0 ns 532.88 ns 592.29 ns 0.6457 7520 B
PureConcat DE125(...)12345 [22] 154.3 ns 2.39 ns 2.66 ns 0.0223 240 B
PureRegex DE125(...)12345 [22] 7,639.8 ns 67.17 ns 68.98 ns - 72 B

BenchmarkFiddle

1
Vlam On

This regex example should work for both DE and BE cases.

using System;
using System.Text.RegularExpressions;
using System.Linq;

class Example
{
   static void Main()
   {
      string input = "DE12500123456789012345";
      string pattern = @"^([A-Z]{2})(?:.{5})(\d{5})(.*)(\d{3})$";

      MatchCollection matches = Regex.Matches(input, pattern);
      
      foreach (Match match in matches)
      {
         Console.Write("{0}*****", match.Groups[1].Value);
         Console.Write("{0}", match.Groups[2].Value);
         Console.Write("{0}", string.Concat(Enumerable.Repeat("*",match.Groups[3].Value.Length)));
         Console.WriteLine("{0}", match.Groups[4].Value);
      }
    }
}