Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get last set of numbers from string, do math, rebuild back into string?

Tags:

c#

regex

.net-4.0

I have a field representing an "Account Number" that is anything but a number most of the time. I need to do some auto-incrementing of these "numbers". Clearly non-ideal for doing math with. The rule that we've decided works for us is that we want to find the right-most group of numbers and auto-increment them by one and return the rebuilt string (even if this makes it one character longer).

Some examples of the numbers are:

  • AC1234 -> AC1235
  • GS3R2C1234 -> GS3R2C1235
  • 1234 -> 1235
  • A-1234 -> A-1235
  • AC1234g -> AC1235g
  • GS3R2C1234g -> GS3R2C1235g
  • 1234g -> 1235g
  • A-1234g -> A-1235g
  • 999 -> 1000
  • GS3R2C9999g -> GS3R2C10000g

I'm working with C#/.NET 4.0. I listed Regex as a tag but that isn't a requirement. This solution need not be in Regular Expressions.

Any thoughts on a good way to do this? Ideal performance isn't a major concern. I'd rather have clear and easy-to-understand/maintain code for this unless it's all wrapped up in a Regex.

Thanks!

like image 975
Jaxidian Avatar asked May 04 '12 16:05

Jaxidian


3 Answers

var src = "ap45245jpb1234h";
var match = Regex.Match(src, @"(?<=(\D|^))\d+(?=\D*$)");
if(match.Success)
{
    var number = int.Parse(match.Value) + 1;
    var newNum=string.Format(
      "{0}{1}{2}",
      src.Substring(0,match.Index),
      number,
      src.Substring(match.Index + match.Length));
    newNum.Dump(); //ap45245jpb1235h
}

Explaining the regex: starting either from (the start of the string) or (a non-digit), match one or more digits that are followed by zero or more non-digits then the end of the string.

Of course, if the extracted number has leading zeros, things will go wrong. I'll leave this as an exercise to the reader.

Using a MatchEvaluator (as suggested by @LB in their answer) this becomes somewhat lighter:

Regex.Replace(
    src,
    @"(?<=(\D|^))\d+(?=\D*$)",
    m => (int.Parse(m.Value)+1).ToString())
like image 155
spender Avatar answered Oct 29 '22 07:10

spender


If I understand you correctly, you would like to add one to the number which is right-most within a certain string.

You could use Regex as others suggested, but since you are trying to do something very specific, Regex will prove slower than implementing an algorithm just for what you do.

You can test this against the Regex solution, and see for yourself that this will be a lot faster:

I ran both 1 million times and timed it with Stopwatch.

Results:

Regex - 10,808,533 ticks

My way - 253,355 ticks

About 40 times faster!!!

Conclusion: Specific solutions for specific problems.

My way is A LOT faster.

And here's the code:

    // Goes through a string from end to start, looking for the last digit character.
    // It then adds 1 to it and returns the result string.
    // If the digit was 9, it turns it to 0 and continues,
    // So the digit before that would be added with one.
    // Overall, it takes the last numeric substring it finds in the string,
    // And replaces it with itself + 1.
    private static unsafe string Foo(string str)
    {
        var added = false;

        fixed (char* pt = str)
        {
            for (var i = str.Length - 1; i >= 0; i--)
            {
                var val = pt[i] - '0';

                // Current char isn't a digit
                if (val < 0 || val > 9)
                {
                    // Digits have been found and processed earlier
                    if (added)
                    {
                        // Add 1 before the digits,
                        // Because if the code reaches this,
                        // It means it was something like 999,
                        // Which should become 1000
                        str = str.Insert(i + 1, "1");
                        break;
                    }

                    continue;
                }

                added = true;

                // Digit isn't 9
                if (val < 9)
                {
                    // Set it to be itself + 1, and break
                    pt[i] = (char)(val + 1 + '0');
                    break;
                }

                // Digit is 9. Set it to be 0 and continue to previous characters
                pt[i] = '0';

                // Reached beginning of string and should add 1 before digits
                if (i == 0)
                {
                    str = str.Insert(0, "1");
                }
            }
        }

        return str;
    }
like image 3
SimpleVar Avatar answered Oct 29 '22 06:10

SimpleVar


Assuming you don't want to replace 1 digit numbers.

string input = "GS3R2C1234g";
var output = Regex.Replace(input, @"\d{2,}$*", m => (Convert.ToInt64(m.Value) + 1).ToString());
like image 2
L.B Avatar answered Oct 29 '22 06:10

L.B