Home > Software engineering >  Regex to add spaces after defined interval
Regex to add spaces after defined interval

Time:01-05

I have the following string:

string str = "***********4123";

I want the output as

**** ****** *4123

The following code is adding spaces after every 4 chars:

Regex.Replace(maskedString, ".{4}", "$0 ");

Is there any way I can add space after the 4th & 6th char?

CodePudding user response:

Working with capturing groups.

Transform to a new value with "{first group} {second group} {remaining}".

string str = "***********4123";
var replaceStr = Regex.Replace(str, @"(\*{4})(\*{6})(.*)", "$1 $2 $3");

Demo @ .NET Fiddle

CodePudding user response:

If you do not insist on using Regex, you can do:

using System;
                    
public class Program
{
    public static void Main()
    {
        string str = "***********4123";
        var strSpan = str.AsSpan();
        Console.WriteLine($"{strSpan[..4]} {strSpan[4..10]} {strSpan[10..]}");
    }
}

Output:

**** ****** *4123

In action: https://dotnetfiddle.net/kKoRJb


Mind that for production, I'd add sanitychecks for the input.

  • IsNullOrWhiteSpace
  • Trim (maybe)
  • Length underrun

Also: If you opt for Regex, consider precompiling it and caching. I did not run benchmarks of this solution against Regex, so you might want to do that if performace is critical here.


Update

So, I was intrigued and did a little Benchmark:

BenchmarkDotNet=v0.13.3, OS=Windows 10 (10.0.19044.2364/21H2/November2021Update)
Intel Core i9-10885H CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=7.0.101
  [Host]     : .NET 7.0.1 (7.0.122.56804), X64 RyuJIT AVX2
  DefaultJob : .NET 7.0.1 (7.0.122.56804), X64 RyuJIT AVX2


|                 Method |     N |        Mean |     Error |    StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|----------------------- |------ |------------:|----------:|----------:|------:|--------:|----------:|------------:|
| RegexVersionUncompiled |  1000 |   336.97 us |  6.616 us |  6.189 us |  7.79 |    0.21 |  62.52 KB |        1.00 |
|   RegexVersionCompiled |  1000 |   252.49 us |  4.904 us |  5.036 us |  5.85 |    0.19 |  62.52 KB |        1.00 |
|            SpanVersion |  1000 |    43.15 us |  0.812 us |  0.834 us |  1.00 |    0.00 |  62.52 KB |        1.00 |
|    StringInsertVersion |  1000 |    33.27 us |  0.655 us |  0.852 us |  0.78 |    0.03 | 117.21 KB |        1.87 |
|                        |       |             |           |           |       |         |           |             |
| RegexVersionUncompiled | 10000 | 3,254.56 us | 54.729 us | 48.515 us |  7.55 |    0.20 | 625.03 KB |        1.00 |
|   RegexVersionCompiled | 10000 | 2,424.43 us | 39.463 us | 32.953 us |  5.62 |    0.14 | 625.03 KB |        1.00 |
|            SpanVersion | 10000 |   432.77 us |  8.456 us |  9.048 us |  1.00 |    0.00 | 625.02 KB |        1.00 |
|    StringInsertVersion | 10000 |   429.94 us |  4.070 us |  3.178 us |  1.00 |    0.02 | 1171.9 KB |        1.87 |

// * Hints *
Outliers
  Benchmark.SpanVersion: Default            -> 4 outliers were removed (45.56 us..71.61 us)
  Benchmark.RegexVersionUncompiled: Default -> 1 outlier  was  removed, 4 outliers were detected (3.16 ms..3.18 ms, 3.37 ms)
  Benchmark.RegexVersionCompiled: Default   -> 2 outliers were removed, 5 outliers were detected (2.36 ms..2.39 ms, 2.48 ms, 3.42 ms)
  Benchmark.StringInsertVersion: Default    -> 3 outliers were removed (441.17 us..460.36 us)

// * Legends *
  N           : Value of the 'N' parameter
  Mean        : Arithmetic mean of all measurements
  Error       : Half of 99.9% confidence interval
  StdDev      : Standard deviation of all measurements
  Ratio       : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD     : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Allocated   : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  Alloc Ratio : Allocated memory ratio distribution ([Current]/[Baseline])
  1 us        : 1 Microsecond (0.000001 sec)

Based on this code where I "stole" the Regex from Yong Shun's answer and the String.Insert Version from Hossein Sabziani' answer:

using BenchmarkDotNet.Attributes;
using Bogus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace RegexBenchmark
{
    [MemoryDiagnoser(false)]
    public class Benchmark
    {
        [Params(1000, 10_000)]
        public int N = 1000;

        private readonly Regex _regex = new (@"(\*{4})(\*{6})(.*)", RegexOptions.Compiled);
        private string[] _inputs;

        [GlobalSetup]
        public void Setup()
        {
            var faker = new Faker();
            _inputs = Enumerable.Range(0, N).Select(_ => faker.Random.ReplaceNumbers("***********####")).ToArray();
        }

        [Benchmark]
        public string[] RegexVersionUncompiled()
        {
            string[] result = new string[N];
            for( int i = 0; i < N; i   ) result[i] = Regex.Replace(_inputs[i], @"(\*{4})(\*{6})(.*)", "$1 $2 $3");
            return result;
        }

        [Benchmark]
        public string[] RegexVersionCompiled()
        {
            string[] result = new string[N];
            for (int i = 0; i < N; i  ) result[i] = _regex.Replace(_inputs[i], "$1 $2 $3");
            return result;
        }

        [Benchmark]
        public string[] SpanVersion()
        {
            string[] result = new string[N];
            for (int i = 0; i < N; i  )
            {
                var strSpan = _inputs[i].AsSpan();
                result[i] = $"{strSpan[..4]} {strSpan[4..10]} {strSpan[10..]}";
            }
            return result;
        }

        [Benchmark]
        public string[] StringInsertVersion()
        {
            string[] result = new string[N];
            for (int i = 0; i < N; i  )
            {
                result[i] = _inputs[i].Insert(4, " ").Insert(11, " ");
            }
            return result;
        }
    }
}

Interestingly: When I switched on display of GC Columns, it seems Regex puts less pressure on the Garbage Collection:

|                 Method |     N |        Mean |     Error |    StdDev | Ratio | RatioSD |     Gen0 |     Gen1 | Allocated | Alloc Ratio |
|----------------------- |------ |------------:|----------:|----------:|------:|--------:|---------:|---------:|----------:|------------:|
| RegexVersionUncompiled |  1000 |   329.92 us |  6.402 us |  8.547 us |  8.55 |    0.29 |   7.3242 |   1.4648 |  62.52 KB |        1.00 |
|   RegexVersionCompiled |  1000 |   244.21 us |  4.637 us |  4.962 us |  6.34 |    0.17 |   7.5684 |   1.7090 |  62.52 KB |        1.00 |
|            SpanVersion |  1000 |    38.60 us |  0.717 us |  0.670 us |  1.00 |    0.00 |   7.6294 |   1.8921 |  62.52 KB |        1.00 |
|    StringInsertVersion |  1000 |    32.69 us |  0.302 us |  0.267 us |  0.85 |    0.02 |  14.3433 |   3.5400 | 117.21 KB |        1.87 |
|                        |       |             |           |           |       |         |          |          |           |             |
| RegexVersionUncompiled | 10000 | 3,242.25 us | 61.809 us | 66.135 us |  7.50 |    0.09 |  74.2188 |  70.3125 | 625.03 KB |        1.00 |
|   RegexVersionCompiled | 10000 | 2,431.65 us | 47.894 us | 44.800 us |  5.64 |    0.13 |  74.2188 |  70.3125 | 625.03 KB |        1.00 |
|            SpanVersion | 10000 |   431.01 us |  5.069 us |  4.741 us |  1.00 |    0.00 |  76.1719 |  75.6836 | 625.02 KB |        1.00 |
|    StringInsertVersion | 10000 |   429.69 us |  7.117 us |  5.943 us |  1.00 |    0.02 | 142.5781 | 142.0898 | 1171.9 KB |        1.87 |

And considering the scaling, I'd probably still go with the Span Solution:

|                 Method |      N |         Mean |      Error |       StdDev | Ratio | RatioSD |      Gen0 |     Gen1 |     Gen2 |   Allocated | Alloc Ratio |
|----------------------- |------- |-------------:|-----------:|-------------:|------:|--------:|----------:|---------:|---------:|------------:|------------:|
| RegexVersionUncompiled |   1000 |    333.54 us |   6.403 us |     6.288 us |  7.69 |    0.15 |    7.3242 |   1.4648 |        - |    62.52 KB |        1.00 |
|   RegexVersionCompiled |   1000 |    239.71 us |   4.601 us |     4.519 us |  5.52 |    0.14 |    7.5684 |   1.7090 |        - |    62.52 KB |        1.00 |
|            SpanVersion |   1000 |     43.35 us |   0.412 us |     0.365 us |  1.00 |    0.00 |    7.6294 |   1.8921 |        - |    62.52 KB |        1.00 |
|    StringInsertVersion |   1000 |     34.18 us |   0.523 us |     0.489 us |  0.79 |    0.01 |   14.3433 |   3.5400 |        - |   117.21 KB |        1.87 |
|                        |        |              |            |              |       |         |           |          |          |             |             |
| RegexVersionUncompiled |  10000 |  3,343.72 us |  66.047 us |    90.406 us |  6.85 |    0.21 |   74.2188 |  70.3125 |        - |   625.03 KB |        1.00 |
|   RegexVersionCompiled |  10000 |  2,450.42 us |  31.348 us |    29.323 us |  5.00 |    0.10 |   74.2188 |  70.3125 |        - |   625.03 KB |        1.00 |
|            SpanVersion |  10000 |    490.05 us |   6.722 us |     6.288 us |  1.00 |    0.00 |   76.1719 |  75.6836 |        - |   625.02 KB |        1.00 |
|    StringInsertVersion |  10000 |    436.04 us |   5.316 us |     4.973 us |  0.89 |    0.02 |  142.5781 | 142.0898 |        - |   1171.9 KB |        1.87 |
|                        |        |              |            |              |       |         |           |          |          |             |             |
| RegexVersionUncompiled | 100000 | 40,730.47 us | 793.225 us | 1,058.932 us |  3.39 |    0.12 |  846.1538 | 769.2308 | 230.7692 |  6251.31 KB |        1.00 |
|   RegexVersionCompiled | 100000 | 32,578.02 us | 645.500 us | 1,163.969 us |  2.74 |    0.14 |  937.5000 | 906.2500 | 281.2500 |  6251.14 KB |        1.00 |
|            SpanVersion | 100000 | 12,016.15 us | 237.602 us |   300.491 us |  1.00 |    0.00 |  968.7500 | 953.1250 | 312.5000 |  6250.24 KB |        1.00 |
|    StringInsertVersion | 100000 | 25,158.93 us | 372.056 us |   329.818 us |  2.09 |    0.06 | 1625.0000 | 968.7500 | 312.5000 | 11719.01 KB |        1.87 |

CodePudding user response:

you can use String.Insert(Int32, String) To add a string at a specific index :

 var result= str.Insert(4, " ").Insert(11, " ");
 Console.WriteLine(result); //"**** ****** *4123"
  • Related