'Regex that gets the last set of numbers from a string

"abc_d1.txt" should get 0
"abc_d1_2.txt" should get 2
"abc_d1_14.txt" should get 14
"abc_d12_x2_156.txt" should get 156

This is what I've done so far, but I am not getting the right result.

  string y = "tester_yg1.txt";
  string pattern = @"(\d+)(?!.*\d)";
  Regex rg = new Regex(pattern);
  var z = rg.Match(fullFileName).Value;
  Console.WriteLine($"z is {z}");


Solution 1:[1]

You can use this regex:

@"(?<=_)\d+(?=\.\w+)|(?<!_\d+)(?=\.\w+)"

Explanation:

(?<=_) look behind for an underscore

\d+ match one or more digits

(?=\.\w+) look ahead for a dot followed by one more word characters (the extension)

| OR

(?<!_\d+) look behind for NOT an underscore followed by one or more digits

(?=\.\w+) look ahead for a dot followed by one more word characters (the extension)

The last part will return an empty match when there's no digit.

Then you will have to convert the empty match to 0 in code.

Update:

To remove the number, you can use this Regex, now call Replace instead:

string pattern1 =  @"_\d+(?=\.\w+)|(?<!_\d+)(?=\.\w+)";
Regex rg1 = new Regex(pattern1);
var z = rg1.Replace(y, "");
string z1  = Path.GetFilename(z);

Now z1 should contain the filename with number and extension removed. When there's no number, it will return the filename without extension.

Solution 2:[2]

Another option is to use a single set of positive lookarounds

(?<=_[^\W\d_]\d+_?)\d*(?=\.\w+$)

Explanation

  • (?<= Positive lookbehind, assert what is directly to the left is
    • _[^\W\d_]\d+_? Match _, a word char except digits or _ and 1+ digits followed by an optional _
  • ) Close lookbehind
  • \d* Match 0+ digits (to also get the position when there is no digit)
  • (?= Positive lookahead, assert what is directly to the right is
    • \.\w+$ Match a . and 1+ word characters till the end of string
  • ) Close lookahead

.NET regex demo

enter image description here

Solution 3:[3]

Use

(?<![^\W_])\d+(?=\.\w+$)

See proof

Explanation

--------------------------------------------------------------------------------
  (?<!                     look behind to see if there is not:
--------------------------------------------------------------------------------
    [^\W_]                   any character except: non-word
                             characters (all but a-z, A-Z, 0-9, _),
                             '_'
--------------------------------------------------------------------------------
  )                        end of look-behind
--------------------------------------------------------------------------------
  \d+                      digits (0-9) (1 or more times (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    \.                       '.'
--------------------------------------------------------------------------------
    \w+                      word characters (a-z, A-Z, 0-9, _) (1 or
                             more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    $                        before an optional \n, and the end of
                             the string
--------------------------------------------------------------------------------
  )                        end of look-ahead

Solution 4:[4]

if using .NET why not just use the Regex \d+ and do regex.Matches(..).LastOrDefault();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Ryszard Czech
Solution 4 Gabriel