Search code examples
c#stringmasking

Masking IBAN codes in C#


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#?


Solution

  • 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